| @@ -0,0 +1,10 @@ | |||
| set(tinyxml2_CXXFLAGS "-D_FORTIFY_SOURCE=2 -O2 -Wno-unused-result") | |||
| set(tinyxml2_CFLAGS "-D_FORTIFY_SOURCE=2 -O2") | |||
| mindspore_add_pkg(tinyxml2 | |||
| VER 8.0.0 | |||
| LIBS tinyxml2 | |||
| URL https://github.com/leethomason/tinyxml2/archive/8.0.0.tar.gz | |||
| CMAKE_OPTION -DCMAKE_BUILD_TYPE=Release | |||
| MD5 5dc535c8b34ee621fe2128f072d275b5) | |||
| include_directories(${tinyxml2_INC}) | |||
| add_library(mindspore::tinyxml2 ALIAS tinyxml2::tinyxml2) | |||
| @@ -56,6 +56,7 @@ if (ENABLE_MINDDATA) | |||
| include(${CMAKE_SOURCE_DIR}/cmake/external_libs/libtiff.cmake) | |||
| include(${CMAKE_SOURCE_DIR}/cmake/external_libs/opencv.cmake) | |||
| include(${CMAKE_SOURCE_DIR}/cmake/external_libs/sqlite.cmake) | |||
| include(${CMAKE_SOURCE_DIR}/cmake/external_libs/tinyxml2.cmake) | |||
| endif() | |||
| include(${CMAKE_SOURCE_DIR}/cmake/external_libs/gtest.cmake) | |||
| @@ -39,6 +39,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows") | |||
| set(opencv_LIBPATH ${opencv_LIBPATH}/../bin/) | |||
| set(jpeg_turbo_LIBPATH ${jpeg_turbo_LIBPATH}/../bin/) | |||
| set(sqlite_LIBPATH ${sqlite_LIBPATH}/../bin/) | |||
| set(tinyxml2_LIBPATH ${tinyxml2_LIBPATH}/../bin/) | |||
| else () | |||
| set(INSTALL_LIB_DIR "lib") | |||
| endif () | |||
| @@ -82,6 +83,15 @@ if (ENABLE_MINDDATA) | |||
| DESTINATION ${INSTALL_LIB_DIR} | |||
| COMPONENT mindspore | |||
| ) | |||
| file(GLOB_RECURSE TINYXML2_LIB_LIST | |||
| ${tinyxml2_LIBPATH}/libtinyxml2* | |||
| ) | |||
| install( | |||
| FILES ${TINYXML2_LIB_LIST} | |||
| DESTINATION ${INSTALL_LIB_DIR} | |||
| COMPONENT mindspore | |||
| ) | |||
| endif () | |||
| if (ENABLE_CPU) | |||
| @@ -90,7 +90,7 @@ else() | |||
| target_link_libraries(_c_dataengine PRIVATE mindspore::pybind11_module -ldl mindspore::protobuf ${SECUREC_LIBRARY}) | |||
| endif() | |||
| target_link_libraries(_c_dataengine PUBLIC mindspore::jpeg_turbo mindspore::opencv_core mindspore::opencv_imgcodecs | |||
| mindspore::opencv_imgproc) | |||
| mindspore::opencv_imgproc mindspore::tinyxml2) | |||
| if (ENABLE_GPUQUE) | |||
| target_link_libraries(_c_dataengine PRIVATE gpu_queue | |||
| ${CUDNN_PATH}/lib64/libcudnn.so | |||
| @@ -898,6 +898,8 @@ Status DEPipeline::ParseVOCOp(const py::dict &args, std::shared_ptr<DatasetOp> * | |||
| std::shared_ptr<VOCOp::Builder> builder = std::make_shared<VOCOp::Builder>(); | |||
| (void)builder->SetDir(ToString(args["dataset_dir"])); | |||
| (void)builder->SetTask(ToString(args["task"])); | |||
| (void)builder->SetMode(ToString(args["mode"])); | |||
| for (auto arg : args) { | |||
| std::string key = py::str(arg.first); | |||
| py::handle value = arg.second; | |||
| @@ -912,6 +914,8 @@ Status DEPipeline::ParseVOCOp(const py::dict &args, std::shared_ptr<DatasetOp> * | |||
| (void)builder->SetSampler(std::move(sampler)); | |||
| } else if (key == "decode") { | |||
| (void)builder->SetDecode(ToBool(value)); | |||
| } else if (key == "class_indexing") { | |||
| (void)builder->SetClassIndex(ToStringMap(value)); | |||
| } | |||
| } | |||
| } | |||
| @@ -55,6 +55,7 @@ | |||
| #include "dataset/engine/datasetops/source/tf_reader_op.h" | |||
| #include "dataset/engine/jagged_connector.h" | |||
| #include "dataset/engine/datasetops/source/text_file_op.h" | |||
| #include "dataset/engine/datasetops/source/voc_op.h" | |||
| #include "dataset/kernels/data/to_float16_op.h" | |||
| #include "dataset/util/random.h" | |||
| #include "mindrecord/include/shard_operator.h" | |||
| @@ -193,6 +194,13 @@ void bindDatasetOps(py::module *m) { | |||
| THROW_IF_ERROR(TextFileOp::CountAllFileRows(filenames, &count)); | |||
| return count; | |||
| }); | |||
| (void)py::class_<VOCOp, DatasetOp, std::shared_ptr<VOCOp>>(*m, "VOCOp") | |||
| .def_static("get_class_indexing", [](const std::string &dir, const std::string &task_type, | |||
| const std::string &task_mode, const py::dict &dict, int64_t numSamples) { | |||
| std::map<std::string, int32_t> output_class_indexing; | |||
| THROW_IF_ERROR(VOCOp::GetClassIndexing(dir, task_type, task_mode, dict, numSamples, &output_class_indexing)); | |||
| return output_class_indexing; | |||
| }); | |||
| } | |||
| void bindTensor(py::module *m) { | |||
| (void)py::class_<GlobalContext>(*m, "GlobalContext") | |||
| @@ -15,8 +15,10 @@ | |||
| */ | |||
| #include "dataset/engine/datasetops/source/voc_op.h" | |||
| #include <algorithm> | |||
| #include <fstream> | |||
| #include <iomanip> | |||
| #include "./tinyxml2.h" | |||
| #include "common/utils.h" | |||
| #include "dataset/core/config_manager.h" | |||
| #include "dataset/core/tensor_shape.h" | |||
| @@ -24,8 +26,24 @@ | |||
| #include "dataset/engine/db_connector.h" | |||
| #include "dataset/engine/execution_tree.h" | |||
| using tinyxml2::XMLDocument; | |||
| using tinyxml2::XMLElement; | |||
| using tinyxml2::XMLError; | |||
| namespace mindspore { | |||
| namespace dataset { | |||
| const char kColumnImage[] = "image"; | |||
| const char kColumnTarget[] = "target"; | |||
| const char kColumnAnnotation[] = "annotation"; | |||
| 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"; | |||
| VOCOp::Builder::Builder() : builder_decode_(false), builder_num_samples_(0), builder_sampler_(nullptr) { | |||
| std::shared_ptr<ConfigManager> cfg = GlobalContext::config_manager(); | |||
| builder_num_workers_ = cfg->num_parallel_workers(); | |||
| @@ -39,13 +57,21 @@ Status VOCOp::Builder::Build(std::shared_ptr<VOCOp> *ptr) { | |||
| builder_sampler_ = std::make_shared<SequentialSampler>(); | |||
| } | |||
| builder_schema_ = std::make_unique<DataSchema>(); | |||
| RETURN_IF_NOT_OK( | |||
| builder_schema_->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); | |||
| RETURN_IF_NOT_OK( | |||
| builder_schema_->AddColumn(ColDescriptor("target", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); | |||
| *ptr = std::make_shared<VOCOp>(builder_num_workers_, builder_rows_per_buffer_, builder_dir_, | |||
| builder_op_connector_size_, builder_num_samples_, builder_decode_, | |||
| std::move(builder_schema_), std::move(builder_sampler_)); | |||
| if (builder_task_type_ == TaskType::Segmentation) { | |||
| RETURN_IF_NOT_OK(builder_schema_->AddColumn( | |||
| ColDescriptor(std::string(kColumnImage), DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); | |||
| RETURN_IF_NOT_OK(builder_schema_->AddColumn( | |||
| ColDescriptor(std::string(kColumnTarget), DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); | |||
| } else if (builder_task_type_ == TaskType::Detection) { | |||
| RETURN_IF_NOT_OK(builder_schema_->AddColumn( | |||
| ColDescriptor(std::string(kColumnImage), DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); | |||
| RETURN_IF_NOT_OK(builder_schema_->AddColumn( | |||
| ColDescriptor(std::string(kColumnAnnotation), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); | |||
| } | |||
| *ptr = std::make_shared<VOCOp>(builder_task_type_, builder_task_mode_, builder_dir_, builder_labels_to_read_, | |||
| builder_num_workers_, builder_rows_per_buffer_, builder_op_connector_size_, | |||
| builder_num_samples_, builder_decode_, std::move(builder_schema_), | |||
| std::move(builder_sampler_)); | |||
| return Status::OK(); | |||
| } | |||
| @@ -58,8 +84,9 @@ Status VOCOp::Builder::SanityCheck() { | |||
| return err_msg.empty() ? Status::OK() : Status(StatusCode::kUnexpectedError, __LINE__, __FILE__, err_msg); | |||
| } | |||
| VOCOp::VOCOp(int32_t num_workers, int32_t rows_per_buffer, const std::string &folder_path, int32_t queue_size, | |||
| int64_t num_samples, bool decode, std::unique_ptr<DataSchema> data_schema, | |||
| VOCOp::VOCOp(const TaskType &task_type, const std::string &task_mode, const std::string &folder_path, | |||
| const std::map<std::string, int32_t> &class_index, int32_t num_workers, int32_t rows_per_buffer, | |||
| int32_t queue_size, int64_t num_samples, bool decode, std::unique_ptr<DataSchema> data_schema, | |||
| std::shared_ptr<Sampler> sampler) | |||
| : ParallelOp(num_workers, queue_size), | |||
| decode_(decode), | |||
| @@ -67,7 +94,10 @@ VOCOp::VOCOp(int32_t num_workers, int32_t rows_per_buffer, const std::string &fo | |||
| buf_cnt_(0), | |||
| num_rows_(0), | |||
| num_samples_(num_samples), | |||
| task_type_(task_type), | |||
| task_mode_(task_mode), | |||
| folder_path_(folder_path), | |||
| class_index_(class_index), | |||
| rows_per_buffer_(rows_per_buffer), | |||
| sampler_(std::move(sampler)), | |||
| data_schema_(std::move(data_schema)) { | |||
| @@ -167,12 +197,25 @@ Status VOCOp::GetNumSamples(int64_t *num) const { | |||
| } | |||
| Status VOCOp::LoadTensorRow(const std::string &image_id, TensorRow *trow) { | |||
| std::shared_ptr<Tensor> image, target; | |||
| const std::string kImageDir = folder_path_ + "/JPEGImages/" + image_id + ".jpg"; | |||
| const std::string kTargetDir = folder_path_ + "/SegmentationClass/" + image_id + ".png"; | |||
| RETURN_IF_NOT_OK(ReadImageToTensor(kImageDir, data_schema_->column(0), &image)); | |||
| RETURN_IF_NOT_OK(ReadImageToTensor(kTargetDir, data_schema_->column(1), &target)); | |||
| (*trow) = {std::move(image), std::move(target)}; | |||
| if (task_type_ == TaskType::Segmentation) { | |||
| std::shared_ptr<Tensor> image, target; | |||
| const std::string kImageFile = | |||
| folder_path_ + std::string(kJPEGImagesFolder) + image_id + std::string(kImageExtension); | |||
| 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) = {std::move(image), std::move(target)}; | |||
| } else if (task_type_ == TaskType::Detection) { | |||
| std::shared_ptr<Tensor> image, annotation; | |||
| const std::string kImageFile = | |||
| folder_path_ + std::string(kJPEGImagesFolder) + image_id + std::string(kImageExtension); | |||
| 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, data_schema_->column(1), &annotation)); | |||
| (*trow) = {std::move(image), std::move(annotation)}; | |||
| } | |||
| return Status::OK(); | |||
| } | |||
| @@ -213,8 +256,13 @@ Status VOCOp::WorkerEntry(int32_t worker_id) { | |||
| } | |||
| Status VOCOp::ParseImageIds() { | |||
| const std::string kImageSets = "/ImageSets/Segmentation/train.txt"; | |||
| std::string image_sets_file = folder_path_ + kImageSets; | |||
| std::string image_sets_file; | |||
| if (task_type_ == TaskType::Segmentation) { | |||
| image_sets_file = | |||
| folder_path_ + std::string(kImageSetsSegmentation) + task_mode_ + std::string(kImageSetsExtension); | |||
| } else if (task_type_ == TaskType::Detection) { | |||
| image_sets_file = folder_path_ + std::string(kImageSetsMain) + task_mode_ + std::string(kImageSetsExtension); | |||
| } | |||
| std::ifstream in_file; | |||
| in_file.open(image_sets_file); | |||
| if (in_file.fail()) { | |||
| @@ -231,6 +279,84 @@ Status VOCOp::ParseImageIds() { | |||
| return Status::OK(); | |||
| } | |||
| Status VOCOp::ParseAnnotationIds() { | |||
| std::vector<std::string> new_image_ids; | |||
| for (auto id : image_ids_) { | |||
| const std::string kAnnotationName = | |||
| folder_path_ + std::string(kAnnotationsFolder) + id + std::string(kAnnotationExtension); | |||
| RETURN_IF_NOT_OK(ParseAnnotationBbox(kAnnotationName)); | |||
| if (label_map_.find(kAnnotationName) != label_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(); | |||
| num_samples_ = (num_samples_ == 0 || num_samples_ > num_rows_) ? num_rows_ : num_samples_; | |||
| return Status::OK(); | |||
| } | |||
| Status VOCOp::ParseAnnotationBbox(const std::string &path) { | |||
| if (!Path(path).Exists()) { | |||
| RETURN_STATUS_UNEXPECTED("File is not found : " + path); | |||
| } | |||
| Bbox bbox; | |||
| XMLDocument doc; | |||
| XMLError e = doc.LoadFile(common::SafeCStr(path)); | |||
| if (e != XMLError::XML_SUCCESS) { | |||
| RETURN_STATUS_UNEXPECTED("Xml load failed"); | |||
| } | |||
| XMLElement *root = doc.RootElement(); | |||
| if (root == nullptr) { | |||
| RETURN_STATUS_UNEXPECTED("Xml load root element error"); | |||
| } | |||
| XMLElement *object = root->FirstChildElement("object"); | |||
| if (object == nullptr) { | |||
| RETURN_STATUS_UNEXPECTED("No object find in " + path); | |||
| } | |||
| while (object != nullptr) { | |||
| std::string label_name; | |||
| uint32_t xmin = 0, ymin = 0, xmax = 0, ymax = 0, truncated = 0, difficult = 0; | |||
| XMLElement *name_node = object->FirstChildElement("name"); | |||
| if (name_node != nullptr) label_name = name_node->GetText(); | |||
| XMLElement *truncated_node = object->FirstChildElement("truncated"); | |||
| if (truncated_node != nullptr) truncated = truncated_node->UnsignedText(); | |||
| XMLElement *difficult_node = object->FirstChildElement("difficult"); | |||
| if (difficult_node != nullptr) difficult = difficult_node->UnsignedText(); | |||
| XMLElement *bbox_node = object->FirstChildElement("bndbox"); | |||
| if (bbox_node != nullptr) { | |||
| XMLElement *xmin_node = bbox_node->FirstChildElement("xmin"); | |||
| if (xmin_node != nullptr) xmin = xmin_node->UnsignedText(); | |||
| XMLElement *ymin_node = bbox_node->FirstChildElement("ymin"); | |||
| if (ymin_node != nullptr) ymin = ymin_node->UnsignedText(); | |||
| XMLElement *xmax_node = bbox_node->FirstChildElement("xmax"); | |||
| if (xmax_node != nullptr) xmax = xmax_node->UnsignedText(); | |||
| XMLElement *ymax_node = bbox_node->FirstChildElement("ymax"); | |||
| if (ymax_node != nullptr) ymax = ymax_node->UnsignedText(); | |||
| } else { | |||
| RETURN_STATUS_UNEXPECTED("bndbox dismatch 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<uint32_t> bbox_list = {xmin, ymin, xmax - xmin, ymax - ymin, truncated, difficult}; | |||
| bbox.emplace_back(std::make_pair(label_name, bbox_list)); | |||
| label_index_[label_name] = 0; | |||
| } | |||
| object = object->NextSiblingElement("object"); | |||
| } | |||
| if (bbox.size() > 0) label_map_[path] = bbox; | |||
| return Status::OK(); | |||
| } | |||
| Status VOCOp::InitSampler() { | |||
| RETURN_IF_NOT_OK(sampler_->HandshakeRandomAccessOp(this)); | |||
| return Status::OK(); | |||
| @@ -245,6 +371,9 @@ Status VOCOp::LaunchThreadsAndInitOp() { | |||
| RETURN_IF_NOT_OK(tree_->LaunchWorkers(num_workers_, std::bind(&VOCOp::WorkerEntry, this, std::placeholders::_1))); | |||
| TaskManager::FindMe()->Post(); | |||
| RETURN_IF_NOT_OK(this->ParseImageIds()); | |||
| if (task_type_ == TaskType::Detection) { | |||
| RETURN_IF_NOT_OK(this->ParseAnnotationIds()); | |||
| } | |||
| RETURN_IF_NOT_OK(this->InitSampler()); | |||
| return Status::OK(); | |||
| } | |||
| @@ -270,6 +399,34 @@ Status VOCOp::ReadImageToTensor(const std::string &path, const ColDescriptor &co | |||
| return Status::OK(); | |||
| } | |||
| Status VOCOp::ReadAnnotationToTensor(const std::string &path, const ColDescriptor &col, | |||
| std::shared_ptr<Tensor> *tensor) { | |||
| Bbox bbox_info = label_map_[path]; | |||
| std::vector<uint32_t> bbox_row; | |||
| dsize_t bbox_column_num = 0, bbox_num = 0; | |||
| for (auto box : bbox_info) { | |||
| if (label_index_.find(box.first) != label_index_.end()) { | |||
| std::vector<uint32_t> bbox; | |||
| if (class_index_.find(box.first) != class_index_.end()) { | |||
| bbox.emplace_back(class_index_[box.first]); | |||
| } else { | |||
| bbox.emplace_back(label_index_[box.first]); | |||
| } | |||
| bbox.insert(bbox.end(), box.second.begin(), box.second.end()); | |||
| bbox_row.insert(bbox_row.end(), bbox.begin(), bbox.end()); | |||
| if (bbox_column_num == 0) { | |||
| bbox_column_num = static_cast<dsize_t>(bbox.size()); | |||
| } | |||
| bbox_num++; | |||
| } | |||
| } | |||
| std::vector<dsize_t> bbox_dim = {bbox_num, bbox_column_num}; | |||
| RETURN_IF_NOT_OK(Tensor::CreateTensor(tensor, col.tensorImpl(), TensorShape(bbox_dim), col.type(), | |||
| reinterpret_cast<unsigned char *>(&bbox_row[0]))); | |||
| return Status::OK(); | |||
| } | |||
| // Derived from RandomAccessOp | |||
| Status VOCOp::GetNumRowsInDataset(int64_t *num) const { | |||
| if (num == nullptr || num_rows_ == 0) { | |||
| @@ -280,5 +437,30 @@ Status VOCOp::GetNumRowsInDataset(int64_t *num) const { | |||
| (*num) = num_rows_; | |||
| return Status::OK(); | |||
| } | |||
| Status VOCOp::GetClassIndexing(const std::string &dir, const std::string &task_type, const std::string &task_mode, | |||
| const py::dict &dict, int64_t numSamples, | |||
| std::map<std::string, int32_t> *output_class_indexing) { | |||
| std::map<std::string, int32_t> input_class_indexing; | |||
| for (auto p : dict) { | |||
| (void)input_class_indexing.insert(std::pair<std::string, int32_t>(py::reinterpret_borrow<py::str>(p.first), | |||
| py::reinterpret_borrow<py::int_>(p.second))); | |||
| } | |||
| if (!input_class_indexing.empty()) { | |||
| *output_class_indexing = input_class_indexing; | |||
| } else { | |||
| std::shared_ptr<VOCOp> op; | |||
| RETURN_IF_NOT_OK( | |||
| Builder().SetDir(dir).SetTask(task_type).SetMode(task_mode).SetClassIndex(input_class_indexing).Build(&op)); | |||
| RETURN_IF_NOT_OK(op->ParseImageIds()); | |||
| RETURN_IF_NOT_OK(op->ParseAnnotationIds()); | |||
| for (const auto label : op->label_index_) { | |||
| (*output_class_indexing).insert(std::make_pair(label.first, label.second)); | |||
| } | |||
| } | |||
| return Status::OK(); | |||
| } | |||
| } // namespace dataset | |||
| } // namespace mindspore | |||
| @@ -16,6 +16,7 @@ | |||
| #ifndef DATASET_ENGINE_DATASETOPS_SOURCE_VOC_OP_H_ | |||
| #define DATASET_ENGINE_DATASETOPS_SOURCE_VOC_OP_H_ | |||
| #include <map> | |||
| #include <memory> | |||
| #include <string> | |||
| #include <utility> | |||
| @@ -39,8 +40,12 @@ namespace dataset { | |||
| template <typename T> | |||
| class Queue; | |||
| using Bbox = std::vector<std::pair<std::string, std::vector<uint32_t>>>; | |||
| class VOCOp : public ParallelOp, public RandomAccessOp { | |||
| public: | |||
| enum class TaskType { Segmentation = 0, Detection = 1 }; | |||
| class Builder { | |||
| public: | |||
| // Constructor for Builder class of ImageFolderOp | |||
| @@ -59,6 +64,34 @@ class VOCOp : public ParallelOp, public RandomAccessOp { | |||
| return *this; | |||
| } | |||
| // Setter method. | |||
| // @param const std::map<std::string, int32_t> &map - a class name to label map | |||
| // @return Builder setter method returns reference to the builder. | |||
| Builder &SetClassIndex(const std::map<std::string, int32_t> &map) { | |||
| builder_labels_to_read_ = map; | |||
| 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 == "Segmentation") { | |||
| builder_task_type_ = TaskType::Segmentation; | |||
| } else if (task_type == "Detection") { | |||
| builder_task_type_ = TaskType::Detection; | |||
| } | |||
| return *this; | |||
| } | |||
| // Setter method. | |||
| // @param const std::string & task_mode | |||
| // @return Builder setter method returns reference to the builder. | |||
| Builder &SetMode(const std::string &task_mode) { | |||
| builder_task_mode_ = task_mode; | |||
| return *this; | |||
| } | |||
| // Setter method. | |||
| // @param int32_t num_workers | |||
| // @return Builder setter method returns reference to the builder. | |||
| @@ -119,25 +152,33 @@ class VOCOp : public ParallelOp, public RandomAccessOp { | |||
| private: | |||
| bool builder_decode_; | |||
| std::string builder_dir_; | |||
| TaskType builder_task_type_; | |||
| std::string builder_task_mode_; | |||
| int32_t builder_num_workers_; | |||
| int32_t builder_op_connector_size_; | |||
| int32_t builder_rows_per_buffer_; | |||
| int64_t builder_num_samples_; | |||
| std::shared_ptr<Sampler> builder_sampler_; | |||
| std::unique_ptr<DataSchema> builder_schema_; | |||
| std::map<std::string, int32_t> builder_labels_to_read_; | |||
| }; | |||
| // 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<std::string, int32_t> class_index - input class-to-index of annotation | |||
| // @param int32_t num_workers - number of workers reading images in parallel | |||
| // @param int32_t rows_per_buffer - number of images (rows) in each buffer | |||
| // @param std::string folder_path - dir directory of VOC | |||
| // @param int32_t queue_size - connector queue size | |||
| // @param int64_t num_samples - number of samples to read | |||
| // @param bool decode - whether to decode images | |||
| // @param std::unique_ptr<DataSchema> data_schema - the schema of the VOC dataset | |||
| // @param std::shared_ptr<Sampler> sampler - sampler tells VOCOp what to read | |||
| VOCOp(int32_t num_workers, int32_t rows_per_buffer, const std::string &folder_path, int32_t queue_size, | |||
| int64_t num_samples, bool decode, std::unique_ptr<DataSchema> data_schema, std::shared_ptr<Sampler> sampler); | |||
| VOCOp(const TaskType &task_type, const std::string &task_mode, const std::string &folder_path, | |||
| const std::map<std::string, int32_t> &class_index, int32_t num_workers, int32_t rows_per_buffer, | |||
| int32_t queue_size, int64_t num_samples, bool decode, std::unique_ptr<DataSchema> data_schema, | |||
| std::shared_ptr<Sampler> sampler); | |||
| // Destructor | |||
| ~VOCOp() = default; | |||
| @@ -167,6 +208,16 @@ class VOCOp : public ParallelOp, public RandomAccessOp { | |||
| // @param show_all | |||
| void Print(std::ostream &out, bool show_all) const override; | |||
| // @param const std::string &dir - VOC dir path | |||
| // @param const std::string &task_type - task type of reading voc job | |||
| // @param const std::string &task_mode - task mode of reading voc job | |||
| // @param const py::dict &dict - input dict of class index | |||
| // @param int64_t numSamples - samples number of VOCDataset | |||
| // @param std::map<std::string, int32_t> *output_class_indexing - output class index of VOCDataset | |||
| static Status GetClassIndexing(const std::string &dir, const std::string &task_type, const std::string &task_mode, | |||
| const py::dict &dict, int64_t numSamples, | |||
| std::map<std::string, int32_t> *output_class_indexing); | |||
| private: | |||
| // Initialize Sampler, calls sampler->Init() within | |||
| // @return Status - The error code return | |||
| @@ -184,19 +235,40 @@ class VOCOp : public ParallelOp, public RandomAccessOp { | |||
| // @return Status - The error code return | |||
| Status ReadImageToTensor(const std::string &path, const ColDescriptor &col, std::shared_ptr<Tensor> *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> tensor - return | |||
| // @return Status - The error code return | |||
| Status ReadAnnotationToTensor(const std::string &path, const ColDescriptor &col, std::shared_ptr<Tensor> *tensor); | |||
| // @param const std::vector<uint64_t> &keys - keys in ioblock | |||
| // @param std::unique_ptr<DataBuffer> db | |||
| // @return Status - The error code return | |||
| Status LoadBuffer(const std::vector<int64_t> &keys, std::unique_ptr<DataBuffer> *db); | |||
| // Read image list from ImageSets | |||
| // @return Status - The error code return | |||
| Status ParseImageIds(); | |||
| // Read annotation from Annotation folder | |||
| // @return Status - The error code return | |||
| Status ParseAnnotationIds(); | |||
| // @param const std::string &path - path to annotation xml | |||
| // @return Status - The error code return | |||
| Status ParseAnnotationBbox(const std::string &path); | |||
| // @param const std::shared_ptr<Tensor> &sample_ids - sample ids of tensor | |||
| // @param std::vector<int64_t> *keys - image id | |||
| // @return Status - The error code return | |||
| Status TraverseSampleIds(const std::shared_ptr<Tensor> &sample_ids, std::vector<int64_t> *keys); | |||
| // Called first when function is called | |||
| // @return Status - The error code return | |||
| Status LaunchThreadsAndInitOp(); | |||
| // Reset dataset state | |||
| // @return Status - The error code return | |||
| Status Reset() override; | |||
| bool decode_; | |||
| @@ -205,6 +277,8 @@ class VOCOp : public ParallelOp, public RandomAccessOp { | |||
| int64_t num_rows_; | |||
| int64_t num_samples_; | |||
| std::string folder_path_; | |||
| TaskType task_type_; | |||
| std::string task_mode_; | |||
| int32_t rows_per_buffer_; | |||
| std::shared_ptr<Sampler> sampler_; | |||
| std::unique_ptr<DataSchema> data_schema_; | |||
| @@ -212,6 +286,9 @@ class VOCOp : public ParallelOp, public RandomAccessOp { | |||
| WaitPost wp_; | |||
| std::vector<std::string> image_ids_; | |||
| QueueList<std::unique_ptr<IOBlock>> io_block_queues_; | |||
| std::map<std::string, int32_t> class_index_; | |||
| std::map<std::string, int32_t> label_index_; | |||
| std::map<std::string, Bbox> label_map_; | |||
| }; | |||
| } // namespace dataset | |||
| } // namespace mindspore | |||
| @@ -34,7 +34,7 @@ import copy | |||
| import numpy as np | |||
| from mindspore._c_dataengine import DataType, TFReaderOp, ImageFolderOp, CifarOp, MnistOp, ManifestOp, \ | |||
| MindRecordOp, TextFileOp, CBatchInfo | |||
| MindRecordOp, TextFileOp, VOCOp, CBatchInfo | |||
| from mindspore._c_expression import typing | |||
| from mindspore import log as logger | |||
| @@ -3454,6 +3454,12 @@ class VOCDataset(SourceDataset): | |||
| Args: | |||
| dataset_dir (str): Path to the root directory that contains the dataset. | |||
| task (str): Set the task type of reading voc data, now only support "Segmentation" or "Detection" | |||
| (default="Segmentation") | |||
| mode(str): Set the data list txt file to be readed (default="train") | |||
| class_indexing (dict, optional): A str-to-int mapping from label name to index | |||
| (default=None, the folder names will be sorted alphabetically and each | |||
| class will be given a unique index starting from 0). | |||
| num_samples (int, optional): The number of images to be included in the dataset | |||
| (default=None, all images). | |||
| num_parallel_workers (int, optional): Number of workers to read the data | |||
| @@ -3469,27 +3475,41 @@ class VOCDataset(SourceDataset): | |||
| argument should be specified only when num_shards is also specified. | |||
| Raises: | |||
| RuntimeError: If xml of Annotations is a invalid format | |||
| RuntimeError: If xml of Annotations loss attribution of "object" | |||
| RuntimeError: If xml of Annotations loss attribution of "bndbox" | |||
| RuntimeError: If sampler and shuffle are specified at the same time. | |||
| RuntimeError: If sampler and sharding are specified at the same time. | |||
| RuntimeError: If num_shards is specified but shard_id is None. | |||
| RuntimeError: If shard_id is specified but num_shards is None. | |||
| ValueError: If task is not equal 'Segmentation' or 'Detection'. | |||
| ValueError: If task equal 'Segmentation' but class_indexing is not None. | |||
| ValueError: If txt related to mode is not exist. | |||
| ValueError: If shard_id is invalid (< 0 or >= num_shards). | |||
| Examples: | |||
| >>> import mindspore.dataset as ds | |||
| >>> dataset_dir = "/path/to/voc_dataset_directory" | |||
| >>> # 1) read all VOC dataset samples in dataset_dir with 8 threads in random order: | |||
| >>> voc_dataset = ds.VOCDataset(dataset_dir, num_parallel_workers=8) | |||
| >>> # 2) read then decode all VOC dataset samples in dataset_dir in sequence: | |||
| >>> voc_dataset = ds.VOCDataset(dataset_dir, decode=True, shuffle=False) | |||
| >>> # in VOC dataset, each dictionary has keys "image" and "target" | |||
| >>> # 1) read VOC data for segmenatation train | |||
| >>> voc_dataset = ds.VOCDataset(dataset_dir, task="Segmentation", mode="train") | |||
| >>> # 2) read VOC data for detection train | |||
| >>> voc_dataset = ds.VOCDataset(dataset_dir, task="Detection", mode="train") | |||
| >>> # 3) read all VOC dataset samples in dataset_dir with 8 threads in random order: | |||
| >>> voc_dataset = ds.VOCDataset(dataset_dir, task="Detection", mode="train", num_parallel_workers=8) | |||
| >>> # 4) read then decode all VOC dataset samples in dataset_dir in sequence: | |||
| >>> voc_dataset = ds.VOCDataset(dataset_dir, task="Detection", mode="train", decode=True, shuffle=False) | |||
| >>> # in VOC dataset, if task='Segmentation', each dictionary has keys "image" and "target" | |||
| >>> # in VOC dataset, if task='Detection', each dictionary has keys "image" and "annotation" | |||
| """ | |||
| @check_vocdataset | |||
| def __init__(self, dataset_dir, num_samples=None, num_parallel_workers=None, | |||
| shuffle=None, decode=False, sampler=None, num_shards=None, shard_id=None): | |||
| def __init__(self, dataset_dir, task="Segmentation", mode="train", class_indexing=None, num_samples=None, | |||
| num_parallel_workers=None, shuffle=None, decode=False, sampler=None, num_shards=None, shard_id=None): | |||
| super().__init__(num_parallel_workers) | |||
| self.dataset_dir = dataset_dir | |||
| self.task = task | |||
| self.mode = mode | |||
| self.class_indexing = class_indexing | |||
| self.sampler = _select_sampler(num_samples, sampler, shuffle, num_shards, shard_id) | |||
| self.num_samples = num_samples | |||
| self.decode = decode | |||
| @@ -3500,6 +3520,9 @@ class VOCDataset(SourceDataset): | |||
| def get_args(self): | |||
| args = super().get_args() | |||
| args["dataset_dir"] = self.dataset_dir | |||
| args["task"] = self.task | |||
| args["mode"] = self.mode | |||
| args["class_indexing"] = self.class_indexing | |||
| args["num_samples"] = self.num_samples | |||
| args["sampler"] = self.sampler | |||
| args["decode"] = self.decode | |||
| @@ -3517,6 +3540,28 @@ class VOCDataset(SourceDataset): | |||
| """ | |||
| return self.num_samples | |||
| def get_class_indexing(self): | |||
| """ | |||
| Get the class index. | |||
| Return: | |||
| Dict, A str-to-int mapping from label name to index. | |||
| """ | |||
| if self.task != "Detection": | |||
| raise NotImplementedError() | |||
| if self.num_samples is None: | |||
| num_samples = 0 | |||
| else: | |||
| num_samples = self.num_samples | |||
| if self.class_indexing is None: | |||
| class_indexing = dict() | |||
| else: | |||
| class_indexing = self.class_indexing | |||
| return VOCOp.get_class_indexing(self.dataset_dir, self.task, self.mode, class_indexing, num_samples) | |||
| class CelebADataset(SourceDataset): | |||
| """ | |||
| @@ -285,9 +285,9 @@ def create_node(node): | |||
| elif dataset_op == 'VOCDataset': | |||
| sampler = construct_sampler(node.get('sampler')) | |||
| pyobj = pyclass(node['dataset_dir'], node.get('num_samples'), node.get('num_parallel_workers'), | |||
| node.get('shuffle'), node.get('decode'), sampler, node.get('num_shards'), | |||
| node.get('shard_id')) | |||
| pyobj = pyclass(node['dataset_dir'], node.get('task'), node.get('mode'), node.get('class_indexing'), | |||
| node.get('num_samples'), node.get('num_parallel_workers'), node.get('shuffle'), | |||
| node.get('decode'), sampler, node.get('num_shards'), node.get('shard_id')) | |||
| elif dataset_op == 'CelebADataset': | |||
| sampler = construct_sampler(node.get('sampler')) | |||
| @@ -455,17 +455,44 @@ def check_vocdataset(method): | |||
| nreq_param_int = ['num_samples', 'num_parallel_workers', 'num_shards', 'shard_id'] | |||
| nreq_param_bool = ['shuffle', 'decode'] | |||
| nreq_param_dict = ['class_indexing'] | |||
| # check dataset_dir; required argument | |||
| dataset_dir = param_dict.get('dataset_dir') | |||
| if dataset_dir is None: | |||
| raise ValueError("dataset_dir is not provided.") | |||
| check_dataset_dir(dataset_dir) | |||
| # check task; required argument | |||
| task = param_dict.get('task') | |||
| if task is None: | |||
| raise ValueError("task is not provided.") | |||
| if not isinstance(task, str): | |||
| raise ValueError("task is not str type.") | |||
| # check mode; required argument | |||
| mode = param_dict.get('mode') | |||
| if mode is None: | |||
| raise ValueError("mode is not provided.") | |||
| if not isinstance(mode, str): | |||
| raise ValueError("mode is not str type.") | |||
| imagesets_file = "" | |||
| if task == "Segmentation": | |||
| imagesets_file = os.path.join(dataset_dir, "ImageSets", "Segmentation", mode + ".txt") | |||
| if param_dict.get('class_indexing') is not None: | |||
| raise ValueError("class_indexing is invalid in Segmentation task") | |||
| elif task == "Detection": | |||
| imagesets_file = os.path.join(dataset_dir, "ImageSets", "Main", mode + ".txt") | |||
| else: | |||
| raise ValueError("Invalid task : " + task) | |||
| check_dataset_file(imagesets_file) | |||
| check_param_type(nreq_param_int, param_dict, int) | |||
| check_param_type(nreq_param_bool, param_dict, bool) | |||
| check_param_type(nreq_param_dict, param_dict, dict) | |||
| check_sampler_shuffle_shard_options(param_dict) | |||
| return method(*args, **kwargs) | |||
| @@ -64,7 +64,7 @@ SET(DE_UT_SRCS | |||
| cifar_op_test.cc | |||
| celeba_op_test.cc | |||
| take_op_test.cc | |||
| text_file_op_test.cc) | |||
| text_file_op_test.cc | |||
| filter_op_test.cc | |||
| ) | |||
| @@ -50,17 +50,170 @@ std::shared_ptr<RepeatOp> Repeat(int repeat_cnt); | |||
| std::shared_ptr<ExecutionTree> Build(std::vector<std::shared_ptr<DatasetOp>> ops); | |||
| std::shared_ptr<VOCOp> CreateVOC(int64_t num_wrks, int64_t rows, int64_t conns, std::string path, | |||
| bool shuf = false, std::unique_ptr<Sampler> sampler = nullptr, | |||
| int64_t num_samples = 0, bool decode = false) { | |||
| std::shared_ptr<VOCOp> so; | |||
| class MindDataTestVOCOp : public UT::DatasetOpTesting { | |||
| protected: | |||
| }; | |||
| TEST_F(MindDataTestVOCOp, TestVOCDetection) { | |||
| // Start with an empty execution tree | |||
| auto my_tree = std::make_shared<ExecutionTree>(); | |||
| std::string dataset_path; | |||
| dataset_path = datasets_root_path_ + "/testVOC2012"; | |||
| std::string task_type("Detection"); | |||
| std::string task_mode("train"); | |||
| std::shared_ptr<VOCOp> my_voc_op; | |||
| VOCOp::Builder builder; | |||
| Status rc = builder.SetNumWorkers(num_wrks).SetDir(path).SetRowsPerBuffer(rows) | |||
| .SetOpConnectorSize(conns).SetSampler(std::move(sampler)) | |||
| .SetNumSamples(num_samples).SetDecode(decode).Build(&so); | |||
| return so; | |||
| Status rc = builder.SetDir(dataset_path) | |||
| .SetTask(task_type) | |||
| .SetMode(task_mode) | |||
| .Build(&my_voc_op); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| rc = my_tree->AssociateNode(my_voc_op); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| rc = my_tree->AssignRoot(my_voc_op); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| MS_LOG(DEBUG) << "Launch tree and begin iteration."; | |||
| rc = my_tree->Prepare(); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| rc = my_tree->Launch(); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| // Start the loop of reading tensors from our pipeline | |||
| DatasetIterator di(my_tree); | |||
| TensorRow tensor_list; | |||
| rc = di.FetchNextTensorRow(&tensor_list); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| int row_count = 0; | |||
| while (!tensor_list.empty()) { | |||
| MS_LOG(DEBUG) << "Row display for row #: " << row_count << "."; | |||
| //Display the tensor by calling the printer on it | |||
| for (int i = 0; i < tensor_list.size(); i++) { | |||
| std::ostringstream ss; | |||
| ss << "(" << tensor_list[i] << "): " << *tensor_list[i] << std::endl; | |||
| MS_LOG(DEBUG) << "Tensor print: " << ss.str() << "."; | |||
| } | |||
| rc = di.FetchNextTensorRow(&tensor_list); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| row_count++; | |||
| } | |||
| ASSERT_EQ(row_count, 9); | |||
| } | |||
| class MindDataTestVOCSampler : public UT::DatasetOpTesting { | |||
| protected: | |||
| }; | |||
| TEST_F(MindDataTestVOCOp, TestVOCSegmentation) { | |||
| // Start with an empty execution tree | |||
| auto my_tree = std::make_shared<ExecutionTree>(); | |||
| std::string dataset_path; | |||
| dataset_path = datasets_root_path_ + "/testVOC2012"; | |||
| std::string task_type("Segmentation"); | |||
| std::string task_mode("train"); | |||
| std::shared_ptr<VOCOp> my_voc_op; | |||
| VOCOp::Builder builder; | |||
| Status rc = builder.SetDir(dataset_path) | |||
| .SetTask(task_type) | |||
| .SetMode(task_mode) | |||
| .Build(&my_voc_op); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| rc = my_tree->AssociateNode(my_voc_op); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| rc = my_tree->AssignRoot(my_voc_op); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| MS_LOG(DEBUG) << "Launch tree and begin iteration."; | |||
| rc = my_tree->Prepare(); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| rc = my_tree->Launch(); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| // Start the loop of reading tensors from our pipeline | |||
| DatasetIterator di(my_tree); | |||
| TensorRow tensor_list; | |||
| rc = di.FetchNextTensorRow(&tensor_list); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| int row_count = 0; | |||
| while (!tensor_list.empty()) { | |||
| MS_LOG(DEBUG) << "Row display for row #: " << row_count << "."; | |||
| //Display the tensor by calling the printer on it | |||
| for (int i = 0; i < tensor_list.size(); i++) { | |||
| std::ostringstream ss; | |||
| ss << "(" << tensor_list[i] << "): " << *tensor_list[i] << std::endl; | |||
| MS_LOG(DEBUG) << "Tensor print: " << ss.str() << "."; | |||
| } | |||
| rc = di.FetchNextTensorRow(&tensor_list); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| row_count++; | |||
| } | |||
| ASSERT_EQ(row_count, 10); | |||
| } | |||
| TEST_F(MindDataTestVOCOp, TestVOCClassIndex) { | |||
| // Start with an empty execution tree | |||
| auto my_tree = std::make_shared<ExecutionTree>(); | |||
| std::string dataset_path; | |||
| dataset_path = datasets_root_path_ + "/testVOC2012"; | |||
| std::string task_type("Detection"); | |||
| std::string task_mode("train"); | |||
| std::map<std::string, int32_t> class_index; | |||
| class_index["car"] = 0; | |||
| class_index["cat"] = 1; | |||
| class_index["train"] = 5; | |||
| std::shared_ptr<VOCOp> my_voc_op; | |||
| VOCOp::Builder builder; | |||
| Status rc = builder.SetDir(dataset_path) | |||
| .SetTask(task_type) | |||
| .SetMode(task_mode) | |||
| .SetClassIndex(class_index) | |||
| .Build(&my_voc_op); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| rc = my_tree->AssociateNode(my_voc_op); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| rc = my_tree->AssignRoot(my_voc_op); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| MS_LOG(DEBUG) << "Launch tree and begin iteration."; | |||
| rc = my_tree->Prepare(); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| rc = my_tree->Launch(); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| // Start the loop of reading tensors from our pipeline | |||
| DatasetIterator di(my_tree); | |||
| TensorRow tensor_list; | |||
| rc = di.FetchNextTensorRow(&tensor_list); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| int row_count = 0; | |||
| while (!tensor_list.empty()) { | |||
| MS_LOG(DEBUG) << "Row display for row #: " << row_count << "."; | |||
| //Display the tensor by calling the printer on it | |||
| for (int i = 0; i < tensor_list.size(); i++) { | |||
| std::ostringstream ss; | |||
| ss << "(" << tensor_list[i] << "): " << *tensor_list[i] << std::endl; | |||
| MS_LOG(DEBUG) << "Tensor print: " << ss.str() << "."; | |||
| } | |||
| rc = di.FetchNextTensorRow(&tensor_list); | |||
| ASSERT_TRUE(rc.IsOk()); | |||
| row_count++; | |||
| } | |||
| ASSERT_EQ(row_count, 6); | |||
| } | |||
| @@ -0,0 +1,51 @@ | |||
| <annotation> | |||
| <folder>VOC2012</folder> | |||
| <filename>32.jpg</filename> | |||
| <source> | |||
| <database>simulate VOC2007 Database</database> | |||
| <annotation>simulate VOC2007</annotation> | |||
| <image>flickr</image> | |||
| </source> | |||
| <size> | |||
| <width>500</width> | |||
| <height>281</height> | |||
| <depth>3</depth> | |||
| </size> | |||
| <segmented>1</segmented> | |||
| <object> | |||
| <name>train</name> | |||
| <pose>Frontal</pose> | |||
| <truncated>0</truncated> | |||
| <difficult>0</difficult> | |||
| <bndbox> | |||
| <xmin>113</xmin> | |||
| <ymin>79</ymin> | |||
| <xmax>323</xmax> | |||
| <ymax>191</ymax> | |||
| </bndbox> | |||
| </object> | |||
| <object> | |||
| <name>train</name> | |||
| <pose>Left</pose> | |||
| <truncated>0</truncated> | |||
| <difficult>0</difficult> | |||
| <bndbox> | |||
| <xmin>121</xmin> | |||
| <ymin>91</ymin> | |||
| <xmax>191</xmax> | |||
| <ymax>121</ymax> | |||
| </bndbox> | |||
| </object> | |||
| <object> | |||
| <name>car</name> | |||
| <pose>Rear</pose> | |||
| <truncated>0</truncated> | |||
| <difficult>0</difficult> | |||
| <bndbox> | |||
| <xmin>195</xmin> | |||
| <ymin>155</ymin> | |||
| <xmax>235</xmax> | |||
| <ymax>235</ymax> | |||
| </bndbox> | |||
| </object> | |||
| </annotation> | |||
| @@ -1,54 +0,0 @@ | |||
| <annotation> | |||
| <folder>VOC2012</folder> | |||
| <filename>27.jpg</filename> | |||
| <source> | |||
| <database>simulate VOC2007 Database</database> | |||
| <annotation>simulate VOC2007</annotation> | |||
| <image>flickr</image> | |||
| </source> | |||
| <size> | |||
| <width>486</width> | |||
| <height>500</height> | |||
| <depth>3</depth> | |||
| </size> | |||
| <segmented>0</segmented> | |||
| <object> | |||
| <name>person</name> | |||
| <pose>Unspecified</pose> | |||
| <truncated>0</truncated> | |||
| <difficult>0</difficult> | |||
| <bndbox> | |||
| <xmin>161</xmin> | |||
| <ymin>132</ymin> | |||
| <xmax>323</xmax> | |||
| <ymax>342</ymax> | |||
| </bndbox> | |||
| <part> | |||
| <name>head</name> | |||
| <bndbox> | |||
| <xmin>159</xmin> | |||
| <ymin>113</ymin> | |||
| <xmax>208</xmax> | |||
| <ymax>166</ymax> | |||
| </bndbox> | |||
| </part> | |||
| <part> | |||
| <name>foot</name> | |||
| <bndbox> | |||
| <xmin>261</xmin> | |||
| <ymin>321</ymin> | |||
| <xmax>287</xmax> | |||
| <ymax>344</ymax> | |||
| </bndbox> | |||
| </part> | |||
| <part> | |||
| <name>foot</name> | |||
| <bndbox> | |||
| <xmin>329</xmin> | |||
| <ymin>317</ymin> | |||
| <xmax>330</xmax> | |||
| <ymax>366</ymax> | |||
| </bndbox> | |||
| </part> | |||
| </object> | |||
| </annotation> | |||
| @@ -0,0 +1 @@ | |||
| invalidxml | |||
| @@ -0,0 +1,15 @@ | |||
| <annotation> | |||
| <folder>VOC2012</folder> | |||
| <filename>33.jpg</filename> | |||
| <source> | |||
| <database>simulate VOC2007 Database</database> | |||
| <annotation>simulate VOC2007</annotation> | |||
| <image>flickr</image> | |||
| </source> | |||
| <size> | |||
| <width>500</width> | |||
| <height>366</height> | |||
| <depth>3</depth> | |||
| </size> | |||
| <segmented>1</segmented> | |||
| </annotation> | |||
| @@ -0,0 +1 @@ | |||
| invalidxml | |||
| @@ -0,0 +1,9 @@ | |||
| 32 | |||
| 33 | |||
| 39 | |||
| 42 | |||
| 61 | |||
| 63 | |||
| 68 | |||
| 121 | |||
| 123 | |||
| @@ -0,0 +1 @@ | |||
| 15 | |||
| @@ -0,0 +1 @@ | |||
| 15 | |||
| @@ -0,0 +1 @@ | |||
| xmlnoobject | |||
| @@ -0,0 +1 @@ | |||
| 4176 | |||
| @@ -15,25 +15,69 @@ | |||
| import mindspore.dataset.transforms.vision.c_transforms as vision | |||
| import mindspore.dataset as ds | |||
| from mindspore import log as logger | |||
| DATA_DIR = "../data/dataset/testVOC2012" | |||
| IMAGE_SHAPE = [2268, 2268, 2268, 2268, 642, 607, 561, 596, 612, 2268] | |||
| TARGET_SHAPE = [680, 680, 680, 680, 642, 607, 561, 596, 612, 680] | |||
| def test_voc_segmentation(): | |||
| data1 = ds.VOCDataset(DATA_DIR, task="Segmentation", mode="train", decode=True, shuffle=False) | |||
| num = 0 | |||
| for item in data1.create_dict_iterator(): | |||
| assert (item["image"].shape[0] == IMAGE_SHAPE[num]) | |||
| assert (item["target"].shape[0] == TARGET_SHAPE[num]) | |||
| num += 1 | |||
| assert (num == 10) | |||
| def test_voc_normal(): | |||
| data1 = ds.VOCDataset(DATA_DIR, decode=True) | |||
| def test_voc_detection(): | |||
| data1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) | |||
| num = 0 | |||
| count = [ 0, 0, 0, 0, 0, 0 ] | |||
| for item in data1.create_dict_iterator(): | |||
| logger.info("item[image] is {}".format(item["image"])) | |||
| logger.info("item[image].shape is {}".format(item["image"].shape)) | |||
| logger.info("item[target] is {}".format(item["target"])) | |||
| logger.info("item[target].shape is {}".format(item["target"].shape)) | |||
| assert (item["image"].shape[0] == IMAGE_SHAPE[num]) | |||
| for bbox in item["annotation"]: | |||
| count[bbox[0]] += 1 | |||
| num += 1 | |||
| logger.info("num is {}".format(str(num))) | |||
| assert (num == 9) | |||
| assert (count == [3,2,1,2,4,3]) | |||
| def test_voc_class_index(): | |||
| class_index = { 'car': 0, 'cat': 1, 'train': 5 } | |||
| data1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", class_indexing=class_index, decode=True) | |||
| class_index1 = data1.get_class_indexing() | |||
| assert (class_index1 == { 'car': 0, 'cat': 1, 'train': 5 }) | |||
| data1 = data1.shuffle(4) | |||
| class_index2 = data1.get_class_indexing() | |||
| assert (class_index2 == { 'car': 0, 'cat': 1, 'train': 5 }) | |||
| num = 0 | |||
| count = [0,0,0,0,0,0] | |||
| for item in data1.create_dict_iterator(): | |||
| for bbox in item["annotation"]: | |||
| assert (bbox[0] == 0 or bbox[0] == 1 or bbox[0] == 5) | |||
| count[bbox[0]] += 1 | |||
| num += 1 | |||
| assert (num == 6) | |||
| assert (count == [3,2,0,0,0,3]) | |||
| def test_voc_get_class_indexing(): | |||
| data1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True) | |||
| class_index1 = data1.get_class_indexing() | |||
| assert (class_index1 == { 'car': 0, 'cat': 1, 'chair': 2, 'dog': 3, 'person': 4, 'train': 5 }) | |||
| data1 = data1.shuffle(4) | |||
| class_index2 = data1.get_class_indexing() | |||
| assert (class_index2 == { 'car': 0, 'cat': 1, 'chair': 2, 'dog': 3, 'person': 4, 'train': 5 }) | |||
| num = 0 | |||
| count = [0,0,0,0,0,0] | |||
| for item in data1.create_dict_iterator(): | |||
| for bbox in item["annotation"]: | |||
| assert (bbox[0] == 0 or bbox[0] == 1 or bbox[0] == 2 or bbox[0] == 3 or bbox[0] == 4 or bbox[0] == 5) | |||
| count[bbox[0]] += 1 | |||
| num += 1 | |||
| assert (num == 9) | |||
| assert (count == [3,2,1,2,4,3]) | |||
| def test_case_0(): | |||
| data1 = ds.VOCDataset(DATA_DIR, decode=True) | |||
| data1 = ds.VOCDataset(DATA_DIR, task="Segmentation", mode="train", decode=True) | |||
| resize_op = vision.Resize((224, 224)) | |||
| @@ -46,7 +90,79 @@ def test_case_0(): | |||
| num = 0 | |||
| for item in data1.create_dict_iterator(): | |||
| logger.info("item[image].shape is {}".format(item["image"].shape)) | |||
| logger.info("item[target].shape is {}".format(item["target"].shape)) | |||
| num += 1 | |||
| logger.info("num is {}".format(str(num))) | |||
| assert (num == 20) | |||
| def test_case_1(): | |||
| data1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True) | |||
| resize_op = vision.Resize((224, 224)) | |||
| data1 = data1.map(input_columns=["image"], operations=resize_op) | |||
| repeat_num = 4 | |||
| data1 = data1.repeat(repeat_num) | |||
| batch_size = 2 | |||
| data1 = data1.batch(batch_size, drop_remainder=True, pad_info={}) | |||
| num = 0 | |||
| for item in data1.create_dict_iterator(): | |||
| num += 1 | |||
| assert (num == 18) | |||
| def test_voc_exception(): | |||
| try: | |||
| data1 = ds.VOCDataset(DATA_DIR, task="InvalidTask", mode="train", decode=True) | |||
| for _ in data1.create_dict_iterator(): | |||
| pass | |||
| assert False | |||
| except ValueError: | |||
| pass | |||
| try: | |||
| data2 = ds.VOCDataset(DATA_DIR, task="Segmentation", mode="train", class_indexing={ "cat":0 }, decode=True) | |||
| for _ in data2.create_dict_iterator(): | |||
| pass | |||
| assert False | |||
| except ValueError: | |||
| pass | |||
| try: | |||
| data3 = ds.VOCDataset(DATA_DIR, task="Detection", mode="notexist", decode=True) | |||
| for _ in data3.create_dict_iterator(): | |||
| pass | |||
| assert False | |||
| except ValueError: | |||
| pass | |||
| try: | |||
| data4 = ds.VOCDataset(DATA_DIR, task="Detection", mode="xmlnotexist", decode=True) | |||
| for _ in data4.create_dict_iterator(): | |||
| pass | |||
| assert False | |||
| except RuntimeError: | |||
| pass | |||
| try: | |||
| data5 = ds.VOCDataset(DATA_DIR, task="Detection", mode="invalidxml", decode=True) | |||
| for _ in data5.create_dict_iterator(): | |||
| pass | |||
| assert False | |||
| except RuntimeError: | |||
| pass | |||
| try: | |||
| data6 = ds.VOCDataset(DATA_DIR, task="Detection", mode="xmlnoobject", decode=True) | |||
| for _ in data6.create_dict_iterator(): | |||
| pass | |||
| assert False | |||
| except RuntimeError: | |||
| pass | |||
| if __name__ == '__main__': | |||
| test_voc_segmentation() | |||
| test_voc_detection() | |||
| test_voc_class_index() | |||
| test_voc_get_class_indexing() | |||
| test_case_0() | |||
| test_case_1() | |||
| test_voc_exception() | |||