mv_machine_learning: add face recognition framework init code
authorInki Dae <inki.dae@samsung.com>
Fri, 25 Feb 2022 10:48:16 +0000 (19:48 +0900)
committerInki Dae <inki.dae@samsung.com>
Fri, 4 Mar 2022 07:55:17 +0000 (16:55 +0900)
Change-Id: I7cda281285c8d3e1b9902f09d21433ed5018942c
Signed-off-by: Inki Dae <inki.dae@samsung.com>
36 files changed:
CMakeLists.txt
include/mv_face_recognition.h [new file with mode: 0644]
include/mv_face_recognition_type.h [new file with mode: 0644]
mv_machine_learning/CMakeLists.txt
mv_machine_learning/face_recognition/CMakeLists.txt [new file with mode: 0644]
mv_machine_learning/face_recognition/include/backbone_model_info.h [new file with mode: 0644]
mv_machine_learning/face_recognition/include/face_net_info.h [new file with mode: 0644]
mv_machine_learning/face_recognition/include/face_recognition.h [new file with mode: 0644]
mv_machine_learning/face_recognition/include/mv_face_recognition_open.h [new file with mode: 0644]
mv_machine_learning/face_recognition/include/nntrainer_dsm.h [new file with mode: 0644]
mv_machine_learning/face_recognition/include/nntrainer_fvm.h [new file with mode: 0644]
mv_machine_learning/face_recognition/include/simple_shot.h [new file with mode: 0644]
mv_machine_learning/face_recognition/src/face_net_info.cpp [new file with mode: 0644]
mv_machine_learning/face_recognition/src/face_recognition.cpp [new file with mode: 0644]
mv_machine_learning/face_recognition/src/mv_face_recognition.cpp [new file with mode: 0644]
mv_machine_learning/face_recognition/src/mv_face_recognition_open.cpp [new file with mode: 0644]
mv_machine_learning/face_recognition/src/nntrainer_dsm.cpp [new file with mode: 0644]
mv_machine_learning/face_recognition/src/nntrainer_fvm.cpp [new file with mode: 0644]
mv_machine_learning/face_recognition/src/simple_shot.cpp [new file with mode: 0644]
mv_machine_learning/inference/include/inference_engine_helper.h [new file with mode: 0644]
mv_machine_learning/inference/src/inference_engine_helper.cpp [new file with mode: 0644]
mv_machine_learning/training/CMakeLists.txt [new file with mode: 0644]
mv_machine_learning/training/include/data_set_manager.h [new file with mode: 0644]
mv_machine_learning/training/include/feature_vector_manager.h [new file with mode: 0644]
mv_machine_learning/training/include/file_util.h [new file with mode: 0644]
mv_machine_learning/training/include/label_manager.h [new file with mode: 0644]
mv_machine_learning/training/include/training_model.h [new file with mode: 0644]
mv_machine_learning/training/src/data_set_manager.cpp [new file with mode: 0644]
mv_machine_learning/training/src/feature_vector_manager.cpp [new file with mode: 0644]
mv_machine_learning/training/src/file_util.cpp [new file with mode: 0644]
mv_machine_learning/training/src/label_manager.cpp [new file with mode: 0644]
mv_machine_learning/training/src/training_model.cpp [new file with mode: 0644]
packaging/capi-media-vision.spec
test/testsuites/machine_learning/CMakeLists.txt
test/testsuites/machine_learning/face_recognition/CMakeLists.txt [new file with mode: 0644]
test/testsuites/machine_learning/face_recognition/test_face_recognition.cpp [new file with mode: 0644]

index 9421c35..2eae70e 100644 (file)
@@ -17,6 +17,10 @@ set(MV_SURVEILLANCE_LIB_NAME "mv_surveillance" CACHE STRING
        "Name of the library will be built for surveillance module (without extension).")
 set(MV_INFERENCE_LIB_NAME "mv_inference" CACHE STRING
        "Name of the library will be built for inference module (without extension).")
+set(MV_TRAINING_LIB_NAME "mv_training" CACHE STRING
+       "Name of the library will be built for training module (without extension).")
+set(MV_FACE_RECOG_LIB_NAME "mv_face_recognition" CACHE STRING
+       "Name of the library will be built for face recognition module (without extension).")
 
 include(FindPkgConfig)
 include(GNUInstallDirs)
diff --git a/include/mv_face_recognition.h b/include/mv_face_recognition.h
new file mode 100644 (file)
index 0000000..ac71923
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics 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 __MEDIAVISION_FACE_RECOGNITION_H__
+#define __MEDIAVISION_FACE_RECOGNITION_H__
+
+#include <mv_common.h>
+#include <mv_face_recognition_type.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @brief Creates face recognition handle.
+ * @details Use this function to create an face recognition handle. After the creation
+ *          the handle has to be prepared with mv_face_recognition_prepare() function to prepare
+ *          face recognition resources.
+ *
+ * @since_tizen 7.0
+ * 
+ * @remarks The @a handle should be released using mv_face_recognition_destroy().
+ *
+ * @param[out] handle    The handle to the face recognition to be created
+ *
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_VISION_ERROR_NONE Successful
+ * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported
+ * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIA_VISION_ERROR_OUT_OF_MEMORY Out of memory
+ *
+ * @see mv_face_recognition_destroy()
+ * @see mv_face_recognition_prepare()
+ */
+int mv_face_recognition_create(mv_face_recognition_h *handle);
+
+/**
+ * @brief Destroy face recognition handle and release all its resources.
+ *
+ * @since_tizen 7.0
+ *
+ * @param[in] handle    The handle to the face recognition object to be destroyed
+ *
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_VISION_ERROR_NONE Successful
+ * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported
+ * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @pre Create face recognition handle by using mv_face_recognition_create()
+ *
+ * @see mv_face_recognition_create()
+ */
+int mv_face_recognition_destroy(mv_face_recognition_h handle);
+
+/**
+ * @brief Prepare the resources for face recognition.
+ * @details Use this function to prepare the resources for face recognition.
+ *
+ * @since_tizen 7.0
+ *
+ * @param[in] handle        The handle to the inference
+ *
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIA_VISION_ERROR_NONE Successful
+ * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported
+ * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int mv_face_recognition_prepare(mv_face_recognition_h handle);
+
+       /**
+        * @brief Register a new face on the @a source
+        * @details Use this function to register a new face.
+        *          Each time when this function is called, a new face on the media source
+        *          will be registered to internal database.
+        *
+        * @since_tizen 7.0
+        *
+        * @param [in] handle         The handle to the face recognition object.
+        * @param [in] source         The handle to the source of the media.
+        * @param [in] label          The label to be registered.
+        * 
+        * @return @c 0 on success, otherwise a negative error value
+        * @retval #MEDIA_VISION_ERROR_NONE Successful
+        * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+        * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT Source colorspace
+        *                                                  isn't supported
+        * @retval #MEDIA_VISION_ERROR_OUT_OF_MEMORY Out of memory
+        *
+        * @pre Create a source handle by calling @ref mv_create_source()
+        * @pre Create an face recognition handle by calling @ref mv_face_recognition_create()
+        * @pre Prepare an face recognition by calling @ref mv_face_recognition_prepare()
+        */
+       int mv_face_recognition_register(mv_face_recognition_h handle, mv_source_h source, const char *label);
+
+       /**
+        * @brief Unregister a new face on the @a source
+        * @details Use this function to unregister a given label.
+        *          Each time when this function is called, all data related to the label
+        *          will be removed from internal database.
+        *
+        * @since_tizen 7.0
+        *
+        * @param [in] handle         The handle to the face recognition object.
+        * @param [in] label          The label to be registered.
+        * 
+        * @return @c 0 on success, otherwise a negative error value
+        * @retval #MEDIA_VISION_ERROR_NONE Successful
+        * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+        * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT Source colorspace
+        *                                                  isn't supported
+        * @retval #MEDIA_VISION_ERROR_OUT_OF_MEMORY Out of memory
+        *
+        * @pre Create an face recognition handle by calling @ref mv_face_recognition_create()
+        * @pre Prepare an face recognition by calling @ref mv_face_recognition_prepare()
+        */
+       int mv_face_recognition_unregister(mv_face_recognition_h handle, const char *label);
+
+       /**
+        * @brief Inference with a given face on the @a source
+        * @details Use this function to inference with a given source.
+        *          This function returns an proper label string to a give source.
+        *
+        * @since_tizen 7.0
+        *
+        * @param [in] handle         The handle to the face recognition object.
+        * @param [in] source         The handle to the source of the media.
+        * @param [out] label         The label to be inferenced.
+        * 
+        * @return @c 0 on success, otherwise a negative error value
+        * @retval #MEDIA_VISION_ERROR_NONE Successful
+        * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+        * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT Source colorspace
+        *                                                  isn't supported
+        * @retval #MEDIA_VISION_ERROR_OUT_OF_MEMORY Out of memory
+        *
+        * @pre Create a source handle by calling @ref mv_create_source()
+        * @pre Create an face recognition handle by calling @ref mv_face_recognition_create()
+        * @pre Prepare an face recognition by calling @ref mv_face_recognition_prepare()
+        * @pre Register a new face by calling @ref mv_face_recognition_register()
+        */
+       int mv_face_recognition_inference(mv_face_recognition_h handle, mv_source_h source, char out_label[MAX_LABEL_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIAVISION_INFERENCE_H__ */
diff --git a/include/mv_face_recognition_type.h b/include/mv_face_recognition_type.h
new file mode 100644 (file)
index 0000000..656eeaf
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics 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 __MEDIAVISION_FACE_RECOGNITION_TYPE_H__
+#define __MEDIAVISION_FACE_RECOGNITION_TYPE_H__
+
+#include <mv_common.h>
+
+#define MAX_LABEL_SIZE 256
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @brief The inference handle.
+ * @details Contains information about location of
+ *          detected landmarks for one or more poses.
+ * @since_tizen 5.5
+ */
+typedef void *mv_face_recognition_h;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MEDIAVISION_FACE_RECOGNITION_TYPE_H__ */
index 37b9e1b..3e99704 100644 (file)
@@ -1 +1,3 @@
 add_subdirectory(inference)
+add_subdirectory(training)
+add_subdirectory(face_recognition)
diff --git a/mv_machine_learning/face_recognition/CMakeLists.txt b/mv_machine_learning/face_recognition/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6fcb15c
--- /dev/null
@@ -0,0 +1,21 @@
+project(${MV_FACE_RECOG_LIB_NAME})
+cmake_minimum_required(VERSION 2.6)
+
+pkg_check_modules(${PROJECT_NAME}_DEP REQUIRED inference-engine-interface-common training-engine-interface-common)
+file(GLOB MV_FACE_RECOG_SOURCE_LIST  "${PROJECT_SOURCE_DIR}/src/*.c" "${PROJECT_SOURCE_DIR}/src/*.cpp")
+
+find_package(OpenCV REQUIRED dnn imgproc)
+if(NOT OpenCV_FOUND)
+       message(SEND_ERROR "OpenCV NOT FOUND")
+       return()
+endif()
+
+if(FORCED_STATIC_BUILD)
+       add_library(${PROJECT_NAME} STATIC ${MV_FACE_RECOG_SOURCE_LIST})
+else()
+       add_library(${PROJECT_NAME} SHARED ${MV_FACE_RECOG_SOURCE_LIST})
+endif()
+
+target_link_libraries(${PROJECT_NAME} ${MV_COMMON_LIB_NAME} ${OpenCV_LIBS} ${${PROJECT_NAME}_DEP_LIBRARIES} mv_inference mv_training)
+target_include_directories(${PROJECT_NAME} PRIVATE include ../inference/include ../training/include)
+install(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR})
diff --git a/mv_machine_learning/face_recognition/include/backbone_model_info.h b/mv_machine_learning/face_recognition/include/backbone_model_info.h
new file mode 100644 (file)
index 0000000..bc53b40
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics 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 BACKBONE_MODEL_INFO_H
+#define BACKBONE_MODEL_INFO_H
+
+#include "inference_engine_common_impl.h"
+
+typedef struct {
+       std::string layer_name;
+       inference_engine_tensor_info tensor_info;
+} model_layer_info;
+
+class BackboneModelInfo {
+public:
+       BackboneModelInfo(std::string model_file_path) { }
+       ~BackboneModelInfo() { }
+
+       virtual std::vector<model_layer_info>& GetInputLayerInfo() { return GetInputLayerInfo(); }
+       virtual std::vector<model_layer_info>& GetOutputLayerInfo() { return GetOutputLayerInfo(); }
+};
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/include/face_net_info.h b/mv_machine_learning/face_recognition/include/face_net_info.h
new file mode 100644 (file)
index 0000000..392a152
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics 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 FACE_NET_INFO_H
+#define FACE_NET_INFO_H
+
+#include "backbone_model_info.h"
+
+class FaceNetInfo : public BackboneModelInfo {
+private:
+       std::vector<model_layer_info> _input_layer_info;
+       std::vector<model_layer_info> _output_layer_info;
+       std::string _model_file_path;
+
+public:
+       FaceNetInfo(std::string model_file_path);
+       ~FaceNetInfo();
+
+       std::string GetModelFilePath();
+       std::vector<model_layer_info>& GetInputLayerInfo();
+       std::vector<model_layer_info>& GetOutputLayerInfo();
+};
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/include/face_recognition.h b/mv_machine_learning/face_recognition/include/face_recognition.h
new file mode 100644 (file)
index 0000000..601dbad
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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 FACE_RECOGNITION_H
+#define FACE_RECOGNITION_H
+
+#include "training_engine_error.h"
+#include "training_engine_common_impl.h"
+#include "inference_engine_common_impl.h"
+#include "inference_engine_helper.h"
+#include "label_manager.h"
+#include "face_net_info.h"
+#include "simple_shot.h"
+
+class FaceRecognition {
+private:
+       std::unique_ptr<DataSetManager> CreateDSM(const training_engine_backend_type_e backend_type);
+       std::unique_ptr<FeatureVectorManager> CreateFVM(const training_engine_backend_type_e backend_type);
+       void UpdateDataSet(std::unique_ptr<DataSetManager>& data_set, std::vector<float>& feature_vec, const int label_idx, const int label_cnt);
+       void UpdateDataSet(std::unique_ptr<DataSetManager>& data_set);
+       int GetAnswer(std::vector<float>& result_tensor, unsigned int *out_idx);
+
+protected:
+       bool _initialized;
+       std::unique_ptr<InferenceEngineHelper> _internal;
+       std::unique_ptr<InferenceEngineHelper> _backbone;
+       std::unique_ptr<FaceNetInfo> _face_net_info;
+       std::unique_ptr<TrainingModel> _training_model;
+       LabelManager _label_manager;
+       std::string _internal_model_file;
+       training_engine_backend_type_e _backend_type;
+
+public:
+       FaceRecognition(const training_engine_backend_type_e backend_type = TRAINING_ENGINE_BACKEND_NNTRAINER,
+                                       const std::string internal_model_file = "model_and_weights.ini");
+       ~ FaceRecognition();
+
+       int Init(const std::string backend_name = "tflite", unsigned int device_type = INFERENCE_TARGET_CPU);
+       int RegisterNewFace(std::vector<float>& vec, std::string label_name);
+       int RecognizeFace(std::vector<float>& vec, unsigned int *out_idx);
+       int DeleteLabel(std::string label_name);
+       int GetLabelWithIndex(std::string& out_label, unsigned int label_idx);
+
+       std::unique_ptr<InferenceEngineHelper>& GetInternal() { return _internal; }
+       std::unique_ptr<InferenceEngineHelper>& GetBackbone() { return _backbone; }
+};
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/include/mv_face_recognition_open.h b/mv_machine_learning/face_recognition/include/mv_face_recognition_open.h
new file mode 100644 (file)
index 0000000..11d4f39
--- /dev/null
@@ -0,0 +1,165 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics 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 __MEDIA_VISION_FACE_RECOGNITION_OPEN_H__
+#define __MEDIA_VISION_FACE_RECOGNITION_OPEN_H__
+
+#include <mv_common.h>
+#include <mv_private.h>
+#include <mv_face_recognition_type.h>
+#include <face_recognition.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+       /**
+        * @brief Create face recognition object handle.
+        * @details Use this function to create an face recognition object handle.
+        *          After creation the handle has to be prepared with
+        *          @ref mv_face_recognition_prepare_open() function to prepare
+        *               an face recognition object.
+        *
+        * @since_tizen 7.0
+        *
+        * @param [out] handle    The handle to the face recognition object to be created
+        *
+        * @return @c 0 on success, otherwise a negative error value
+        * @retval #MEDIA_VISION_ERROR_NONE Successful
+        * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+        * @retval #MEDIA_VISION_ERROR_OUT_OF_MEMORY Out of memory
+        *
+        * @post Release @a handle by using
+        *       @ref mv_face_recognition_destroy_open() function when it is not needed
+        *       anymore
+        *
+        * @see mv_face_recognition_destroy_open()
+        */
+       int mv_face_recognition_create_open(mv_face_recognition_h *out_handle);
+
+       /**
+        * @brief Destroy face recognition handle and releases all its resources.
+        *
+        * @since_tizen 7.0
+        *
+        * @param [in] handle    The handle to the face recognition object to be destroyed.
+        *
+        * @return @c 0 on success, otherwise a negative error value
+        * @retval #MEDIA_VISION_ERROR_NONE Successful
+        * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+        *
+        * @pre Create an face recognition handle by using @ref mv_face_recognition_create_open()
+        *
+        * @see mv_face_recognition_create_open()
+        */
+       int mv_face_recognition_destroy_open(mv_face_recognition_h handle);
+
+       /**
+        * @brief Prepare face recognition.
+        *
+        * @since_tizen 7.0
+        *
+        * @param [in] handle    The handle to the face recognition object to be prepared.
+        *
+        * @return @c 0 on success, otherwise a negative error value
+        * @retval #MEDIA_VISION_ERROR_NONE Successful
+        * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+        *
+        * @pre Create an face recognition handle by using @ref mv_face_recognition_create_open()
+        *
+        * @see mv_face_recognition_create_open()
+        */
+       int mv_face_recognition_prepare_open(mv_face_recognition_h handle);
+
+       /**
+        * @brief Register a new face on the @a source
+        * @details Use this function to register a new face.
+        *          Each time when this function is called, a new face on the media source
+        *          will be registered to internal database.
+        *
+        * @since_tizen 7.0
+        *
+        * @param [in] handle         The handle to the face recognition object.
+        * @param [in] source         The handle to the source of the media.
+        * @param [in] label          The label to be registered.
+        * 
+        * @return @c 0 on success, otherwise a negative error value
+        * @retval #MEDIA_VISION_ERROR_NONE Successful
+        * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+        * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT Source colorspace
+        *                                                  isn't supported
+        * @retval #MEDIA_VISION_ERROR_OUT_OF_MEMORY Out of memory
+        *
+        * @pre Create a source handle by calling @ref mv_create_source()
+        * @pre Create an face recognition handle by calling @ref mv_face_recognition_create_open()
+        * @pre Prepare an face recognition by calling @ref mv_face_recognition_prepare_open()
+        */
+       int mv_face_recognition_register_open(mv_face_recognition_h handle, mv_source_h source, const char *label);
+
+       /**
+        * @brief Unregister a new face on the @a source
+        * @details Use this function to unregister a given label.
+        *          Each time when this function is called, all data related to the label
+        *          will be removed from internal database.
+        *
+        * @since_tizen 7.0
+        *
+        * @param [in] handle         The handle to the face recognition object.
+        * @param [in] label          The label to be registered.
+        * 
+        * @return @c 0 on success, otherwise a negative error value
+        * @retval #MEDIA_VISION_ERROR_NONE Successful
+        * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+        * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT Source colorspace
+        *                                                  isn't supported
+        * @retval #MEDIA_VISION_ERROR_OUT_OF_MEMORY Out of memory
+        *
+        * @pre Create an face recognition handle by calling @ref mv_face_recognition_create_open()
+        * @pre Prepare an face recognition by calling @ref mv_face_recognition_prepare_open()
+        */
+       int mv_face_recognition_unregister_open(mv_face_recognition_h handle, const char *label);
+
+       /**
+        * @brief Inference with a given face on the @a source
+        * @details Use this function to inference with a given source.
+        *          This function returns an proper label string to a give source.
+        *
+        * @since_tizen 7.0
+        *
+        * @param [in] handle         The handle to the face recognition object.
+        * @param [in] source         The handle to the source of the media.
+        * @param [out] label         The label to be inferenced.
+        * 
+        * @return @c 0 on success, otherwise a negative error value
+        * @retval #MEDIA_VISION_ERROR_NONE Successful
+        * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter
+        * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT Source colorspace
+        *                                                  isn't supported
+        * @retval #MEDIA_VISION_ERROR_OUT_OF_MEMORY Out of memory
+        *
+        * @pre Create a source handle by calling @ref mv_create_source()
+        * @pre Create an face recognition handle by calling @ref mv_face_recognition_create_open()
+        * @pre Prepare an face recognition by calling @ref mv_face_recognition_prepare_open()
+        * @pre Register a new face by calling @ref mv_face_recognition_register_open()
+        */
+       int mv_face_recognition_inference_open(mv_face_recognition_h handle, mv_source_h source, char out_label[MAX_LABEL_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MEDIA_VISION_INFERENCE_OPEN_H__ */
diff --git a/mv_machine_learning/face_recognition/include/nntrainer_dsm.h b/mv_machine_learning/face_recognition/include/nntrainer_dsm.h
new file mode 100644 (file)
index 0000000..98af865
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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 NNTRAINER_DSM_H
+#define NNTRAINER_DSM_H
+
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+#include "feature_vector_manager.h"
+#include "data_set_manager.h"
+
+class NNTrainerDSM : public DataSetManager {
+private:
+       void PrintHeader(FeaVecHeader& fvh);
+
+public:
+       NNTrainerDSM();
+       ~NNTrainerDSM();
+
+       void LoadDataSet(const std::string file_name);
+};
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/include/nntrainer_fvm.h b/mv_machine_learning/face_recognition/include/nntrainer_fvm.h
new file mode 100644 (file)
index 0000000..b486d3e
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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 NNTRAINER_FVM_H
+#define NNTRAINER_FVM_H
+
+#include <string.h>
+#include <iostream>
+#include <fstream>
+#include <istream>
+#include <algorithm>
+#include <vector>
+#include <map>
+
+#include "feature_vector_manager.h"
+#include "file_util.h"
+
+class NNTrainerFVM : public FeatureVectorManager {
+public:
+       NNTrainerFVM(const std::string feature_vector_file = "feature_vector_file.dat");
+       ~NNTrainerFVM();
+
+       void WriteHeader(size_t feature_size, size_t one_hot_table_size, unsigned int  data_set_cnt);
+       void ReadHeader(FeaVecHeader& header);
+       void WriteFeatureVec(std::vector<float>& feature_vec, const int max_label, const int label_index);
+};
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/include/simple_shot.h b/mv_machine_learning/face_recognition/include/simple_shot.h
new file mode 100644 (file)
index 0000000..a95f734
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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 SIMPLE_SHOT_H
+#define SIMPLE_SHOT_H
+
+#include "training_model.h"
+
+class SimpleShot : public TrainingModel {
+private:
+       TrainingEngineBackendInfo _engine_info;
+
+public:
+       SimpleShot(const training_engine_backend_type_e backend_type = TRAINING_ENGINE_BACKEND_NNTRAINER,
+                          const std::string internal_model_file = "model_and_weights.ini");
+       ~SimpleShot();
+
+       // Configure layers for SimpleShot learning.
+       void ConfigureModel(int num_of_class);
+
+       void UpdateDataSet(const std::string data_file_path);
+
+       void Compile();
+
+       void Training();
+
+       void SaveModel(const std::string file_path);
+
+       TrainingEngineBackendInfo& GetTrainingEngineInfo() { return _engine_info; }
+};
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/src/face_net_info.cpp b/mv_machine_learning/face_recognition/src/face_net_info.cpp
new file mode 100644 (file)
index 0000000..f223ec1
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics 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.
+ */
+
+#include "face_net_info.h"
+
+using namespace std;
+
+FaceNetInfo::FaceNetInfo(std::string model_file_path) : BackboneModelInfo(model_file_path)
+{
+       _model_file_path = model_file_path;
+
+       const std::string input_layer_name = {  "input" };
+       const inference_engine_tensor_info input_tensor_info = {
+               { 112, 112, 3, 1 },
+               INFERENCE_TENSOR_SHAPE_NCHW,
+               INFERENCE_TENSOR_DATA_TYPE_FLOAT32,
+               (size_t)(1 * 3 * 112 * 112)
+       };
+
+       model_layer_info input_info = { input_layer_name, input_tensor_info };
+       _input_layer_info.push_back(input_info);
+
+       const std::string output_layer_name = { "embeddings" };
+       const inference_engine_tensor_info output_tensor_info = {
+               { 192, 1, 1, 1 },
+               INFERENCE_TENSOR_SHAPE_NCHW,
+               INFERENCE_TENSOR_DATA_TYPE_FLOAT32,
+               (size_t)(1 * 192)
+       };
+
+       model_layer_info output_info = { output_layer_name, output_tensor_info };
+       _output_layer_info.push_back(output_info);
+}
+
+FaceNetInfo::~FaceNetInfo()
+{
+       _input_layer_info.clear();
+       _output_layer_info.clear();
+}
+
+std::string FaceNetInfo::GetModelFilePath()
+{
+       return _model_file_path;
+}
+
+std::vector<model_layer_info>& FaceNetInfo::GetInputLayerInfo()
+{
+       return _input_layer_info;
+}
+
+std::vector<model_layer_info>& FaceNetInfo::GetOutputLayerInfo()
+{
+       return _output_layer_info;
+}
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/src/face_recognition.cpp b/mv_machine_learning/face_recognition/src/face_recognition.cpp
new file mode 100644 (file)
index 0000000..e8b0fcb
--- /dev/null
@@ -0,0 +1,489 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include <string.h>
+#include <fstream>
+#include <istream>
+#include <tuple>
+#include <map>
+#include <algorithm>
+
+#include <sys/stat.h>
+
+#include <dlog.h>
+#include <mv_common.h>
+#include <mv_private.h>
+
+#include "face_recognition.h"
+#include "nntrainer_fvm.h"
+#include "nntrainer_dsm.h"
+#include "file_util.h"
+
+using namespace std;
+using namespace TrainingEngineInterface::Common;
+
+FaceRecognition::FaceRecognition(const training_engine_backend_type_e backend_type, const string internal_model_file) :
+               _initialized(), _internal(), _backbone(), _internal_model_file(internal_model_file)
+{
+       try {
+               _face_net_info = make_unique<FaceNetInfo>("/usr/share/capi-media-vision/models/FD/tflite/facenet.tflite");
+               _training_model = make_unique<SimpleShot>(backend_type, _internal_model_file);
+               _backend_type = backend_type;
+
+               // Update label manager from a given label file.
+               int cnt = _label_manager.ImportLabel();
+
+               LOGD("%d labels have been imported", cnt);
+       } catch (string& exception) {
+               LOGE("%s", exception.c_str());
+               return;
+       } catch (const exception& e) {
+               LOGE("%s", e.what());
+               return;
+       }
+}
+
+FaceRecognition::~FaceRecognition()
+{
+       _label_manager.Clear();
+}
+
+unique_ptr<DataSetManager> FaceRecognition::CreateDSM(const training_engine_backend_type_e backend_type)
+{
+       switch(backend_type) {
+       case TRAINING_ENGINE_BACKEND_NNTRAINER:
+               return make_unique<NNTrainerDSM>();
+       default:
+               throw string("Invalid training engine backend type.");
+       }
+
+       throw string("Invalid training engine backend type.");
+}
+
+unique_ptr<FeatureVectorManager> FaceRecognition::CreateFVM(const training_engine_backend_type_e backend_type)
+{
+       switch(backend_type) {
+       case TRAINING_ENGINE_BACKEND_NNTRAINER:
+               return make_unique<NNTrainerFVM>();
+       default:
+               throw string("Invalid training engine backend type.");
+       }
+
+       throw string("Invalid training engine backend type.");
+}
+
+void FaceRecognition::UpdateDataSet(unique_ptr<DataSetManager>& data_set, std::vector<float>& feature_vec, const int label_idx, const int label_cnt)
+{
+       try {
+               size_t data_set_cnt = 0;
+               auto fvm = CreateFVM(_backend_type);
+
+               data_set = CreateDSM(_backend_type);
+
+               // 1. If data set file exists then load the file to DataSetManager object first
+               //    and them write them to the data set file with updated label value, and then
+               //    write a new dataset to the data set file.
+               //    Otherwise, it writes only new data set to the data set file.
+               if (FaceRecogUtil::IsFileExist(fvm->GetFileName())) {
+                       data_set->LoadDataSet(fvm->GetFileName());
+
+                       vector<vector<float>> feature_vectors = data_set->GetData();
+                       vector<unsigned int> label_idx_vectors = data_set->GetLabelIdx();
+
+                       // 1) Remove existing data file.
+                       remove(fvm->GetFileName().c_str());
+
+                       // 2) Write existing feature vectors and its one-hot encoding table considered
+                       //    for new label count to the data set file.
+                       for (unsigned int idx = 0; idx < feature_vectors.size(); ++idx)
+                               fvm->WriteFeatureVec(feature_vectors[idx], label_cnt, label_idx_vectors[idx]);
+
+                       data_set_cnt += feature_vectors.size();
+
+                       // 3) If same feature vector isn't duplcated then write the feature vector to data set file.
+                       if (!data_set->IsFeatureVectorDuplicated(feature_vec)) {
+                               fvm->WriteFeatureVec(feature_vec, label_cnt, label_idx);
+                               LOGD("Added a new feature vector to data set file.");
+                               data_set_cnt++;
+                       }
+               } else {
+                       // 1) Write only a new data set to the data st file.
+                       fvm->WriteFeatureVec(feature_vec, label_cnt, label_idx);
+                       LOGD("Added a new feature vector to data set file.");
+                       data_set_cnt++;
+               }
+
+               // 2. Write feature vector header.
+               fvm->WriteHeader(feature_vec.size(), label_cnt, data_set_cnt);
+               data_set->Clear();
+
+               data_set->LoadDataSet(fvm->GetFileName());
+       } catch (const string& exception) {
+               throw exception;
+       } catch (const exception& e) {
+               throw e;
+       }
+}
+
+void FaceRecognition::UpdateDataSet(unique_ptr<DataSetManager>& data_set)
+{
+       data_set = CreateDSM(_backend_type);
+
+       try {
+               auto fvm = CreateFVM(_backend_type);
+
+               if (FaceRecogUtil::IsFileExist(fvm->GetFileName()) == false)
+                       throw string("Feature vector file not found.");
+
+               data_set->LoadDataSet(fvm->GetFileName());
+       } catch (const string& exception) {
+               throw exception;
+       } catch (const exception& e) {
+               throw e;
+       }
+}
+
+int FaceRecognition::Init(const string backend_name, unsigned int device_type)
+{
+       if (_initialized) {
+               LOGW("Already initialized.");
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       }
+
+       try {
+               // Initialize inference engine object for backbone model.
+               _backbone = make_unique<InferenceEngineHelper>(backend_name, device_type);
+
+               vector<string> input_layer_names, output_layer_names;
+               vector<inference_engine_tensor_info> input_tensor_info, output_tensor_info;
+
+               for (auto& input : _face_net_info->GetInputLayerInfo()) {
+                       input_layer_names.push_back(input.layer_name);
+                       input_tensor_info.push_back(input.tensor_info);
+               }
+
+               for (auto& output : _face_net_info->GetOutputLayerInfo()) {
+                       output_layer_names.push_back(output.layer_name);
+                       output_tensor_info.push_back(output.tensor_info);
+               }
+
+               _backbone->UpdateLayerInfo(input_layer_names, output_layer_names,
+                                                                  input_tensor_info, output_tensor_info);
+
+               _backbone->Load(_face_net_info->GetModelFilePath());
+
+               TrainingEngineBackendInfo engine_info = _training_model->GetTrainingEngineInfo();
+
+               _internal = make_unique<InferenceEngineHelper>(engine_info.backend_name, engine_info.target_device);
+       } catch (const string& exception) {
+               LOGE("%s", exception.c_str());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       } catch (const std::exception& e) {
+               LOGE("%s", e.what());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       }
+
+       _initialized = true;
+
+       return MEDIA_VISION_ERROR_NONE;
+}
+
+int FaceRecognition::RegisterNewFace(vector<float>& vec, string label_name)
+{
+       if (!_initialized) {
+               LOGE("Initialization not ready yet.");
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       }
+
+       try {
+               // 1. Store only label names to label file, which aren't duplicated.
+               bool duplicated  = _label_manager.AddLabelToMap(label_name, label_name);
+               if (!duplicated) {
+                       int ret = _label_manager.AddLabelToFile(label_name);
+                       if (ret == 0)
+                               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+               }
+
+               if (_face_net_info->GetInputLayerInfo().empty() || _face_net_info->GetInputLayerInfo().size() > 1) {
+                       LOGE("Invalid input layer size - input layer size should be 1.");
+                       return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+               }
+
+               if (_face_net_info->GetOutputLayerInfo().empty() || _face_net_info->GetOutputLayerInfo().size() > 1) {
+                       LOGE("Invalid output layer size - output layer size should be 1.");
+                       return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+               }
+
+               // Set input tensor for inference.
+               vector<model_layer_info>& input_layer_info = _face_net_info->GetInputLayerInfo();
+
+               // Ps. input layer size should be 1.
+               _backbone->UpdateInputData(input_layer_info[0].layer_name, vec, vec.size() * 4);
+
+               int ret = _backbone->Run();
+               if (ret != INFERENCE_ENGINE_ERROR_NONE) {
+                       LOGE("fail to inference backbone model.");
+                       return MEDIA_VISION_ERROR_INVALID_OPERATION;
+               }
+
+               vector<float> feature_vec;
+               // Set input tensor for inference.
+               vector<model_layer_info>& output_layer_info = _face_net_info->GetOutputLayerInfo();
+
+               // 2. Get feature vector from a given vec through inference engine.
+               // Ps. output layer size should be 1.
+               ret = _backbone->GetVectorFromOutput(output_layer_info[0].layer_name, feature_vec);
+               if (ret != INFERENCE_ENGINE_ERROR_NONE) {
+                       LOGE("fail to get output vector.");
+                       return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+               }
+
+               // Get label index and count.
+               int label_idx = _label_manager.GetLabelIndex(label_name);
+               int label_cnt = _label_manager.GetMaxLabel();
+
+               _training_model->ConfigureModel(label_cnt);
+
+               unique_ptr<DataSetManager> data_set;
+
+               UpdateDataSet(data_set, feature_vec, label_idx, label_cnt);
+               _training_model->ApplyDataSet(data_set);
+               _training_model->Compile();
+               _training_model->Training();
+       } catch (string& exception) {
+               LOGE("%s", exception.c_str());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       } catch (const std::exception& e) {
+               LOGE("%s", e.what());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       }
+
+       return MEDIA_VISION_ERROR_NONE;
+}
+
+int FaceRecognition::GetAnswer(vector<float>& result_tensor, unsigned int *out_idx)
+{
+       int answer_idx;
+
+       string result_str;
+
+       for (auto& r : result_tensor)
+               result_str += to_string(r) + " ";
+
+       LOGD("raw data = %s", result_str.c_str());
+
+       answer_idx = max_element(result_tensor.begin(), result_tensor.end()) - result_tensor.begin();
+
+       // Check decision threshold.
+       if (result_tensor[answer_idx] < _label_manager.GetDecisionThreshold()) {
+               return MEDIA_VISION_ERROR_NO_DATA;
+       }
+
+#if 0
+       float weighted = result_tensor[answer_idx] * _label_manager.GetDecisionWeight();
+
+       // Check weighted value.
+       for (auto& r : result_tensor) {
+               if (result_tensor[answer_idx] == r)
+                       continue;
+
+               if (weighted < r)
+                       return MEDIA_VISION_ERROR_NO_DATA;
+       }
+#endif
+
+       *out_idx = answer_idx;
+
+       return MEDIA_VISION_ERROR_NONE;
+}
+
+int FaceRecognition::RecognizeFace(vector<float>& vec, unsigned int *out_idx)
+{
+       if (!_initialized) {
+               LOGE("Initialization not ready yet.");
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       }
+
+       TrainingEngineBackendInfo engine_info = _training_model->GetTrainingEngineInfo();
+       vector<string>& input_layers = engine_info.input_layer_names;
+       vector<inference_engine_tensor_info>& input_tensor_info = engine_info.input_tensor_info;
+       vector<string>& output_layers = engine_info.output_layer_names;
+       vector<inference_engine_tensor_info>& output_tensor_info = engine_info.output_tensor_info;
+       vector<float> result_tensor;
+
+       try {
+               if (_face_net_info->GetInputLayerInfo().empty() || _face_net_info->GetInputLayerInfo().size() > 1) {
+                       LOGE("Invalid input layer size - input layer size should be 1.");
+                       return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+               }
+
+               if (_face_net_info->GetOutputLayerInfo().empty() || _face_net_info->GetOutputLayerInfo().size() > 1) {
+                       LOGE("Invalid output layer size - output layer size should be 1.");
+                       return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+               }
+
+               // Set input tensor for inference.
+               vector<model_layer_info>& input_layer_info = _face_net_info->GetInputLayerInfo();
+
+               // Ps. input layer size should be 1.
+               _backbone->UpdateInputData(input_layer_info[0].layer_name, vec, vec.size() * 4);
+
+               int ret = _backbone->Run();
+               if (ret != INFERENCE_ENGINE_ERROR_NONE) {
+                       LOGE("Fail to Run.");
+                       return MEDIA_VISION_ERROR_INVALID_OPERATION;
+               }
+
+               vector<float> feature_vec;
+               // Set input tensor for inference.
+               vector<model_layer_info>& output_layer_info = _face_net_info->GetOutputLayerInfo();
+
+               // 2. Get feature vector from a given vec through inference engine.
+               // Ps. output layer size should be 1.
+               ret = _backbone->GetVectorFromOutput(output_layer_info[0].layer_name, feature_vec);
+               if (ret != INFERENCE_ENGINE_ERROR_NONE) {
+                       LOGE("Fail to GetVectorFromOutput.");
+                       return MEDIA_VISION_ERROR_INVALID_OPERATION;
+               }
+
+               output_tensor_info[0].shape[0] = _label_manager.GetMaxLabel();
+               _internal->UpdateLayerInfo(input_layers, output_layers, input_tensor_info, output_tensor_info);
+
+               // model file could be created in runtime so just return -1 if the model file doesn't exist.
+               ret = _internal->Load(_internal_model_file);
+               if (ret != INFERENCE_ENGINE_ERROR_NONE) {
+                       LOGE("Fail to Load.");
+                       return MEDIA_VISION_ERROR_INVALID_OPERATION;
+               }
+
+               _internal->UpdateInputData(input_layers[0], feature_vec, feature_vec.size() * 4);
+
+               ret = _internal->Run();
+               if (ret != INFERENCE_ENGINE_ERROR_NONE) {
+                       LOGE("Fail to Run.");
+                       return MEDIA_VISION_ERROR_INVALID_OPERATION;
+               }
+
+               ret = _internal->GetVectorFromOutput(output_layers[0], result_tensor);
+               if (ret != INFERENCE_ENGINE_ERROR_NONE) {
+                       LOGE("Fail to GetVectorFromOutput.");
+                       return MEDIA_VISION_ERROR_INVALID_OPERATION;
+               }
+
+               return GetAnswer(result_tensor, out_idx);
+       } catch (const string& exception) {
+               LOGE("%s", exception.c_str());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       } catch (const exception& e) {
+               LOGE("%s", e.what());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       }
+
+       return MEDIA_VISION_ERROR_INVALID_OPERATION;
+}
+
+int FaceRecognition::DeleteLabel(std::string label_name)
+{
+       // Deleting a given label is to remove existing registered person from label and feature vector files.
+
+       try {
+               if (_label_manager.IsExist(label_name) == false) {
+                       LOGE("%s doesn't exsit in label file.", label_name.c_str());
+                       return MEDIA_VISION_ERROR_NO_DATA;
+               }
+
+               unsigned int target_label_idx = _label_manager.GetLabelIndex(label_name);
+
+               // Get label count after removing a given label from the label file.
+               _label_manager.RemoveLabel(label_name);
+
+               int label_cnt = _label_manager.GetMaxLabel();
+               auto fvm = CreateFVM(_backend_type);
+               auto data_set = CreateDSM(_backend_type);
+
+               data_set->LoadDataSet(fvm->GetFileName());
+
+               // Remove existing data file.
+               remove(fvm->GetFileName().c_str());
+
+               vector<vector<float>> feature_vectors = data_set->GetData();
+               vector<unsigned int> label_idx_vectors = data_set->GetLabelIdx();
+
+               size_t data_set_cnt = 0;
+
+               // Write existing feature vectors and its one-hot encoding table with updated label.
+               for (unsigned int idx = 0; idx < feature_vectors.size(); ++idx) {
+                       // Except the data sets with a given target_label_idx.
+                       if (label_idx_vectors[idx] == target_label_idx)
+                               continue;
+
+                       // One-hot encoding table should be updated.
+                       // Assume that below label file exists for example,
+                       //     In label file
+                       //     -------------
+                       //     offset 0 : label 1
+                       //     offset 1 : label 2
+                       //     offset 2 : label 3
+                       //
+                       //     One hot encoding table should be updated like below after removing label 1,
+                       //     In label file
+                       //     -------------
+                       //     offset 0 : label 2
+                       //     offset 1 : label 3
+                       //
+                       // So if the index of removed label less than remaining index then decrease each index.
+                       if (label_idx_vectors[idx] > target_label_idx)
+                               label_idx_vectors[idx]--;
+
+                       fvm->WriteFeatureVec(feature_vectors[idx], label_cnt, label_idx_vectors[idx]);
+                       data_set_cnt++;
+               }
+
+               fvm->WriteHeader(feature_vectors[0].size(), label_cnt, data_set_cnt);
+
+               _training_model->ConfigureModel(label_cnt);
+
+               std::unique_ptr<DataSetManager> new_data_set;
+
+               UpdateDataSet(new_data_set);
+               _training_model->ApplyDataSet(new_data_set);
+               _training_model->Compile();
+               _training_model->Training();
+       } catch (const string& exception) {
+               LOGE("%s", exception.c_str());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       } catch (const exception& e) {
+               LOGE("%s", e.what());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       }
+
+       return MEDIA_VISION_ERROR_NONE;
+}
+
+int FaceRecognition::GetLabelWithIndex(string& out_label, unsigned int label_idx)
+{
+       try {
+               _label_manager.GetLabelString(out_label, label_idx);
+       } catch (const string& exception) {
+               LOGE("%s", exception.c_str());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       } catch (const exception& e) {
+               LOGE("%s", e.what());
+               return MEDIA_VISION_ERROR_INVALID_OPERATION;
+       }
+
+       return MEDIA_VISION_ERROR_NONE;
+}
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/src/mv_face_recognition.cpp b/mv_machine_learning/face_recognition/src/mv_face_recognition.cpp
new file mode 100644 (file)
index 0000000..558476f
--- /dev/null
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics 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.
+ */
+
+#include "mv_private.h"
+#include "mv_face_recognition.h"
+#include "mv_face_recognition_open.h"
+
+int mv_face_recognition_create(mv_face_recognition_h *out_handle)
+{
+       MEDIA_VISION_SUPPORT_CHECK(
+               __mv_inference_face_check_system_info_feature_supported());
+
+       MEDIA_VISION_NULL_ARG_CHECK(out_handle);
+
+       MEDIA_VISION_FUNCTION_ENTER();
+
+       int ret = MEDIA_VISION_ERROR_NONE;
+
+       ret = mv_face_recognition_create_open(out_handle);
+
+       MEDIA_VISION_FUNCTION_LEAVE();
+
+       return ret;
+}
+
+int mv_face_recognition_destroy(mv_face_recognition_h handle)
+{
+       MEDIA_VISION_SUPPORT_CHECK(
+               __mv_inference_face_check_system_info_feature_supported());
+
+       MEDIA_VISION_INSTANCE_CHECK(handle);
+
+       MEDIA_VISION_FUNCTION_ENTER();
+
+       int ret = MEDIA_VISION_ERROR_NONE;
+
+       ret = mv_face_recognition_destroy_open(handle);
+
+       MEDIA_VISION_FUNCTION_LEAVE();
+
+       return ret;
+}
+
+int mv_face_recognition_prepare(mv_face_recognition_h handle)
+{
+       MEDIA_VISION_SUPPORT_CHECK(
+               __mv_inference_face_check_system_info_feature_supported());
+
+       MEDIA_VISION_INSTANCE_CHECK(handle);
+
+       MEDIA_VISION_FUNCTION_ENTER();
+
+       int ret = MEDIA_VISION_ERROR_NONE;
+
+       ret = mv_face_recognition_prepare_open(handle);
+
+       MEDIA_VISION_FUNCTION_LEAVE();
+
+       return ret;
+}
+
+int mv_face_recognition_register(mv_face_recognition_h handle, mv_source_h source, const char *label)
+{
+       MEDIA_VISION_SUPPORT_CHECK(
+               __mv_inference_face_check_system_info_feature_supported());
+
+       MEDIA_VISION_INSTANCE_CHECK(handle);
+       MEDIA_VISION_INSTANCE_CHECK(source);
+       MEDIA_VISION_INSTANCE_CHECK(label);
+
+       MEDIA_VISION_FUNCTION_ENTER();
+
+       int ret = MEDIA_VISION_ERROR_NONE;
+
+       ret = mv_face_recognition_register_open(handle, source, label);
+
+       MEDIA_VISION_FUNCTION_LEAVE();
+
+       return ret;
+}
+
+int mv_face_recognition_unregister(mv_face_recognition_h handle, const char *label)
+{
+       MEDIA_VISION_SUPPORT_CHECK(
+               __mv_inference_face_check_system_info_feature_supported());
+
+       MEDIA_VISION_INSTANCE_CHECK(handle);
+       MEDIA_VISION_INSTANCE_CHECK(label);
+
+       MEDIA_VISION_FUNCTION_ENTER();
+
+       int ret = MEDIA_VISION_ERROR_NONE;
+
+       ret = mv_face_recognition_unregister_open(handle, label);
+
+       MEDIA_VISION_FUNCTION_LEAVE();
+
+       return ret;
+}
+
+int mv_face_recognition_inference(mv_face_recognition_h handle, mv_source_h source, char *out_label)
+{
+       MEDIA_VISION_SUPPORT_CHECK(
+               __mv_inference_face_check_system_info_feature_supported());
+
+       MEDIA_VISION_INSTANCE_CHECK(handle);
+       MEDIA_VISION_INSTANCE_CHECK(source);
+       MEDIA_VISION_INSTANCE_CHECK(out_label);
+
+       MEDIA_VISION_FUNCTION_ENTER();
+
+       int ret = MEDIA_VISION_ERROR_NONE;
+
+       ret = mv_face_recognition_inference_open(handle, source, out_label);
+
+       MEDIA_VISION_FUNCTION_LEAVE();
+
+       return ret;
+}
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/src/mv_face_recognition_open.cpp b/mv_machine_learning/face_recognition/src/mv_face_recognition_open.cpp
new file mode 100644 (file)
index 0000000..8cd569e
--- /dev/null
@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics 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.
+ */
+
+#include <dlog.h>
+
+#include "face_recognition.h"
+#include "feature_vector_manager.h"
+#include "mv_face_recognition_open.h"
+
+using namespace std;
+
+int mv_face_recognition_create_open(mv_face_recognition_h *handle)
+{
+       if (handle == NULL) {
+               LOGE("Handle can't be created because handle pointer is NULL");
+               return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+       }
+
+       (*handle) = static_cast<mv_face_recognition_h>(new (std::nothrow)FaceRecognition());
+
+       if (*handle == NULL) {
+               LOGE("Failed to create face recognition handle");
+               return MEDIA_VISION_ERROR_OUT_OF_MEMORY;
+       }
+
+       LOGD("face recognition handle [%p] has been created", *handle);
+
+       return MEDIA_VISION_ERROR_NONE;
+}
+
+int mv_face_recognition_destroy_open(mv_face_recognition_h handle)
+{
+       if (!handle) {
+               LOGE("Handle is NULL.");
+               return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+       }
+
+       LOGD("Destroying face recognition handle [%p]", handle);
+       delete static_cast<FaceRecognition *>(handle);
+       LOGD("Face recognition handle has been destroyed");
+
+       return MEDIA_VISION_ERROR_NONE;
+}
+
+int mv_face_recognition_prepare_open(mv_face_recognition_h handle)
+{
+       LOGD("ENTER");
+
+       if (!handle) {
+               LOGE("Handle is NULL.");
+               return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+       }
+
+       FaceRecognition *pFace = static_cast<FaceRecognition *>(handle);
+
+       int ret = pFace->Init();
+       if (ret != MEDIA_VISION_ERROR_NONE) {
+               LOGE("Fail to initialize face recognition.");
+               return ret;
+       }
+
+       LOGD("LEAVE");
+
+       return ret;
+}
+
+int mv_face_recognition_register_open(mv_face_recognition_h handle, mv_source_h source, const char *label)
+{
+       LOGD("ENTER");
+
+       if (!handle) {
+               LOGE("Handle is NULL.");
+               return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+       }
+
+       FaceRecognition *pFace = static_cast<FaceRecognition *>(handle);
+       mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID;
+       unsigned int width = 0, height = 0, bufferSize = 0;
+       unsigned char *buffer = NULL;
+
+       if (mv_source_get_width(source, &width) != MEDIA_VISION_ERROR_NONE ||
+               mv_source_get_height(source, &height) != MEDIA_VISION_ERROR_NONE ||
+               mv_source_get_colorspace(source, &colorspace) != MEDIA_VISION_ERROR_NONE ||
+               mv_source_get_buffer(source, &buffer, &bufferSize))
+                       return MEDIA_VISION_ERROR_INTERNAL;
+
+       // TODO. Let's support various color spaces.
+
+       if (colorspace != MEDIA_VISION_COLORSPACE_RGB888) {
+               LOGE("Not Supported format!\n");
+               return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT;
+       }
+
+       vector<float> src_vec;
+
+       FeatureVectorManager::GetVecFromRGB(buffer, src_vec, width, height, 112, 112);
+
+       int ret = pFace->RegisterNewFace(src_vec, string(label));
+       if (ret != MEDIA_VISION_ERROR_NONE) {
+               LOGE("Fail to register new face.");
+               return ret;
+       }
+
+       LOGD("LEAVE");
+
+       return ret;
+}
+
+int mv_face_recognition_unregister_open(mv_face_recognition_h handle, const char *label)
+{
+       LOGD("ENTER");
+
+       if (!handle) {
+               LOGE("Handle is NULL.");
+               return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+       }
+
+       FaceRecognition *pFace = static_cast<FaceRecognition *>(handle);
+
+       int ret = pFace->DeleteLabel(string(label));
+       if (ret != MEDIA_VISION_ERROR_NONE) {
+               LOGE("Fail to register new face.");
+               return ret;
+       }
+
+       LOGD("LEAVE");
+       return 0;
+}
+
+int mv_face_recognition_inference_open(mv_face_recognition_h handle, mv_source_h source, char *out_label)
+{
+       LOGD("ENTER");
+
+       if (!handle) {
+               LOGE("Handle is NULL.");
+               return MEDIA_VISION_ERROR_INVALID_PARAMETER;
+       }
+
+       FaceRecognition *pFace = static_cast<FaceRecognition *>(handle);
+       mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID;
+       unsigned int width = 0, height = 0, bufferSize = 0;
+       unsigned char *buffer = NULL;
+
+       if (mv_source_get_width(source, &width) != MEDIA_VISION_ERROR_NONE ||
+               mv_source_get_height(source, &height) != MEDIA_VISION_ERROR_NONE ||
+               mv_source_get_colorspace(source, &colorspace) != MEDIA_VISION_ERROR_NONE ||
+               mv_source_get_buffer(source, &buffer, &bufferSize))
+                       return MEDIA_VISION_ERROR_INTERNAL;
+
+       // TODO. Let's support various color spaces.
+
+       if (colorspace != MEDIA_VISION_COLORSPACE_RGB888) {
+               LOGE("Not Supported format!\n");
+               return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT;
+       }
+
+       vector<float> src_vec;
+       unsigned int out_idx;
+
+       FeatureVectorManager::GetVecFromRGB(buffer, src_vec, width, height, 112, 112);
+
+       int ret = pFace->RecognizeFace(src_vec, &out_idx);
+       if (ret == MEDIA_VISION_ERROR_NO_DATA) {
+               LOGW("Label not found.");
+               out_label = NULL;
+               return ret;
+       }
+
+       if (ret != MEDIA_VISION_ERROR_NONE) {
+               LOGE("Fail to recognize face.");
+               return ret;
+       }
+
+       string result_label;
+
+       ret = pFace->GetLabelWithIndex(result_label, out_idx);
+       if (ret != MEDIA_VISION_ERROR_NONE) {
+               LOGI("Fail to find label.");
+               return ret;
+       }
+
+       int length = result_label.copy(out_label, result_label.length(), 0);
+       out_label[length] = '\0';
+
+       LOGD("LEAVE");
+
+       return MEDIA_VISION_ERROR_NONE;
+}
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/src/nntrainer_dsm.cpp b/mv_machine_learning/face_recognition/src/nntrainer_dsm.cpp
new file mode 100644 (file)
index 0000000..34a5bb8
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include <dlog.h>
+#include <mv_private.h>
+
+#include "nntrainer_dsm.h"
+
+using namespace std;
+
+void NNTrainerDSM::PrintHeader(FeaVecHeader& fvh)
+{
+       LOGD("signature = %u", fvh.signature);
+       LOGD("feature vector size = %zu", fvh.feature_size);
+       LOGD("one hot encoding table size = %zu", fvh.one_hot_table_size);
+       LOGD("data set count = %u", fvh.data_set_cnt);
+}
+
+NNTrainerDSM::NNTrainerDSM() : DataSetManager()
+{
+
+}
+
+NNTrainerDSM::~NNTrainerDSM()
+{
+
+}
+
+void NNTrainerDSM::LoadDataSet(const std::string file_name)
+{
+       std::ifstream openFile(file_name);
+
+       if (!openFile.is_open())
+               throw std::string("fail to open a file.");
+
+       // Feature vector file header is written at the end of the data file
+       // So read feature vector header from the end of the file.
+       openFile.seekg(static_cast<int>(sizeof(FeaVecHeader) * -1), std::ios::end);
+
+       FeaVecHeader fvh;
+
+       openFile.read((char *)&fvh, sizeof(FeaVecHeader));
+       openFile.seekg(0, std::ios::beg);
+
+       PrintHeader(fvh);
+
+       size_t line_size_in_bytes = fvh.feature_size * 4 + fvh.one_hot_table_size * 4;
+
+       _feature_vector_size = fvh.feature_size;
+       _label_size = fvh.one_hot_table_size;
+       _data_set_length = line_size_in_bytes;
+
+       float *line_data = new(std::nothrow) float[line_size_in_bytes];
+       if (!line_data)
+               throw std::string("fail to allocate line_data.");
+
+       for (size_t data_set_idx = 0; data_set_idx < fvh.data_set_cnt; ++data_set_idx) {
+               openFile.read((char *)line_data, line_size_in_bytes);
+
+               std::vector<float> data, label;
+
+               for (size_t num = 0; num < fvh.feature_size; ++num)
+                       data.push_back((float)line_data[num]);
+
+               int label_idx = 0;
+
+               for (size_t num = 0; num < fvh.one_hot_table_size; ++num) {
+                       if (line_data[fvh.feature_size + num] == 1.0f)
+                               label_idx = num;
+
+                       label.push_back((float)line_data[fvh.feature_size + num]);
+               }
+
+               _data.push_back(data);
+               _labels.push_back(label);
+               _label_index.push_back(label_idx);
+       }
+
+       delete line_data;
+       openFile.close();
+}
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/src/nntrainer_fvm.cpp b/mv_machine_learning/face_recognition/src/nntrainer_fvm.cpp
new file mode 100644 (file)
index 0000000..68df940
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include "nntrainer_fvm.h"
+
+using namespace std;
+
+NNTrainerFVM::NNTrainerFVM(const std::string feature_vector_file) : FeatureVectorManager(feature_vector_file)
+{
+
+}
+
+NNTrainerFVM::~NNTrainerFVM()
+{
+
+}
+
+void NNTrainerFVM::WriteHeader(size_t feature_size, size_t one_hot_table_size, unsigned int  data_set_cnt)
+{
+       std::ofstream writeFile;
+
+       writeFile.open(_feature_vector_file.c_str(), std::ios::out | std::ios::binary | std::ios::app);
+       if (!writeFile.is_open())
+               throw std::string("fail to open a file");
+
+       FeaVecHeader header = { feature_vector_signagure, feature_size, one_hot_table_size, data_set_cnt };
+
+       writeFile.write((char *)&header, sizeof(FeaVecHeader));
+
+       writeFile.close();
+}
+
+void NNTrainerFVM::ReadHeader(FeaVecHeader& header)
+{
+       std::ifstream openFile(_feature_vector_file.c_str(), std::ios::in | std::ios::binary);
+
+       if (!openFile.is_open())
+               throw std::string("fail to open a file.");
+
+       openFile.seekg(static_cast<int>(sizeof(FeaVecHeader) * -1), std::ios::end);
+
+       openFile.read((char *)&header, sizeof(FeaVecHeader));
+       if (!openFile)
+               throw std::string("fail to read feature vector file.");
+
+       openFile.close();
+
+       if (header.signature != feature_vector_signagure)
+               throw std::string("wrong feature vector file header.");
+}
+
+void NNTrainerFVM::WriteFeatureVec(std::vector<float>& feature_vec, const int max_label, const int label_index)
+{
+       std::ofstream writeFile;
+
+       writeFile.open(_feature_vector_file.c_str(), std::ios::out | std::ios::binary | std::ios::app);
+
+       if (!writeFile.is_open())
+               throw std::string("fail to open a file.");
+
+       size_t img_size = feature_vec.size() * 4;
+       std::unique_ptr<char[]> pBuffer = std::make_unique<char[]>(img_size);
+       if (!pBuffer)
+               throw std::string("fail to allocate buffer.");
+
+       memcpy(pBuffer.get(), feature_vec.data(), img_size);
+       writeFile.write(pBuffer.get(), img_size);
+
+       for (int idx = 0; idx < max_label; ++idx) {
+               float one_hot_table = 0.0f;
+
+               if (label_index == idx)
+                       one_hot_table = 1.0f;
+
+               writeFile.write((char *)&one_hot_table, sizeof(float));
+       }
+
+       writeFile.close();
+}
\ No newline at end of file
diff --git a/mv_machine_learning/face_recognition/src/simple_shot.cpp b/mv_machine_learning/face_recognition/src/simple_shot.cpp
new file mode 100644 (file)
index 0000000..cfbae23
--- /dev/null
@@ -0,0 +1,139 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include <string.h>
+#include <fstream>
+#include <istream>
+#include <tuple>
+#include <map>
+#include <algorithm>
+
+#include <sys/stat.h>
+
+#include "simple_shot.h"
+#include "data_set_manager.h"
+#include "feature_vector_manager.h"
+
+using namespace std;
+using namespace TrainingEngineInterface::Common;
+
+SimpleShot::SimpleShot(const training_engine_backend_type_e backend_type, const std::string internal_model_file) :
+                                               TrainingModel(backend_type, internal_model_file)
+{
+       map<int, string>::iterator item = _backend_lookup.find(backend_type);
+       if (item == _backend_lookup.end())
+               throw string("Invalid training engine backend type.");
+
+       _engine_info.backend_name = item->second;
+       _engine_info.target_device = INFERENCE_TARGET_CPU;
+
+       const inference_engine_tensor_info nntrainer_input_tensor_info = {
+               { 192, 1, 1, 1 },
+               INFERENCE_TENSOR_SHAPE_NCHW,
+               INFERENCE_TENSOR_DATA_TYPE_FLOAT32,
+               (size_t)(192 * 1 * 1 * 1)
+       };
+
+       _engine_info.input_layer_names.push_back("preprocess_l2norm0");
+       _engine_info.input_tensor_info.push_back(nntrainer_input_tensor_info);
+
+       // size of output tensor will be updated by RecognizeFace function
+       // because the size should be changed according to maximum label count
+       // so it has 1 in default.
+       inference_engine_tensor_info nntrainer_output_tensor_info = {
+               std::vector<size_t>{ 1, 1, 1, 1 },
+               INFERENCE_TENSOR_SHAPE_NCHW,
+               INFERENCE_TENSOR_DATA_TYPE_FLOAT32,
+               1
+       };
+
+       _engine_info.output_layer_names.push_back("centroid_knn1");
+       _engine_info.output_tensor_info.push_back(nntrainer_output_tensor_info);
+
+       _engine_info.optimizer_property = {. options = { "learning_rate=0.1" } };
+       _engine_info.compile_property = { .options = { "batch_size=1" } };
+}
+
+SimpleShot::~SimpleShot()
+{
+       // If a model exists then destroy the model.
+       if (_model)
+               _training->DestroyModel(_model.get());
+}
+
+void SimpleShot::ConfigureModel(int num_of_class)
+{
+       training_engine_config config = { _engine_info.backend_name };
+       int ret = _training->BindBackend(config);
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw std::string("Fail to bind backend engine.");
+
+       training_engine_capacity capacity = { TRAINING_TENSOR_SHAPE_MIN };
+       ret = _training->GetBackendCapacity(capacity);
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw std::string("Fail to get backend capacity.");
+
+       _model = _training->CreateModel();
+       if (!_model)
+               throw std::string("Fail to create a model.");
+
+       auto l2norm = _training->CreateLayer(TRAINING_LAYER_TYPE_L2NORM);
+       if (!l2norm)
+               throw std::string("Fail to create l2norm layer.");
+
+       auto knn = _training->CreateLayer(TRAINING_LAYER_TYPE_CENTROID_KNN);
+       if (!knn)
+               throw std::string("Fail to create knn layer.");
+
+       // Ps. In case of the first layer, input_shape property is mandatorily required.
+       // 1:192 is a shape of backbone model output tensor.
+       training_engine_layer_property l2norm_property = { .options = { "input_shape=1:192", "trainable=false" } };
+       ret = _training->SetLayerProperty(l2norm.get(), l2norm_property);
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw std::string("Fail to set layer propery.");
+
+       const std::string num_class_prop = "num_class=" + std::to_string(num_of_class);
+       training_engine_layer_property knn_property = { .options = { num_class_prop, "trainable=false" } };
+
+       ret = _training->SetLayerProperty(knn.get(), knn_property);
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw std::string("Fail to set layer property.");
+
+       ret = _training->AddLayer(_model.get(), l2norm.get());
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw std::string("Fail to add l2norm layer.");
+
+       ret = _training->AddLayer(_model.get(), knn.get());
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw std::string("Fail to add knn layer.");
+}
+
+void SimpleShot::SaveModel(const std::string file_path)
+{
+    int ret = TRAINING_ENGINE_ERROR_NONE;
+
+    std::string bin_file_path = file_path.substr(0, file_path.find('.'));
+    bin_file_path += std::string(".bin");
+
+    // NNStreamer returns an error if internal model file(ini and bin files) exists before generating it.
+    // So remove existing files.
+    std::remove(bin_file_path.c_str());
+    std::remove(file_path.c_str());
+
+    ret = _training->SaveModel(_model.get(), file_path);
+    if (ret != TRAINING_ENGINE_ERROR_NONE)
+        throw std::string("Fail to save a model.");
+}
diff --git a/mv_machine_learning/inference/include/inference_engine_helper.h b/mv_machine_learning/inference/include/inference_engine_helper.h
new file mode 100644 (file)
index 0000000..179ca96
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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 INFERENCE_ENGINE_HELPER_H
+#define INFERENCE_ENGINE_HELPER_H
+
+#include <iostream>
+#include <memory>
+
+#include "inference_engine_error.h"
+#include "inference_engine_common_impl.h"
+
+class InferenceEngineHelper {
+private:
+       std::unique_ptr<InferenceEngineInterface::Common::InferenceEngineCommon> _engine;
+       IETensorBuffer _inputs, _outputs;
+       inference_engine_layer_property _input_property, _output_property;
+
+       int GetModelInfo(std::vector<std::string> &model_paths,
+                                        std::vector<std::string> &models);
+       int PrepareTensorBuffers(InferenceEngineInterface::Common::InferenceEngineCommon *engine,
+                                                        IETensorBuffer &inputs,
+                                                        IETensorBuffer &outputs);
+
+       void CleanupTensorBuffers(IETensorBuffer &inputs, IETensorBuffer &outputs);
+
+public:
+       InferenceEngineHelper(std::string backend_name, int target_device);
+       ~InferenceEngineHelper();
+       int Load(std::string backbone_path);
+       int UpdateLayerInfo(const std::vector<std::string>& input_layers,
+                                               const std::vector<std::string>& output_layers,
+                                               const std::vector<inference_engine_tensor_info>& input_tensor_info,
+                                               const std::vector<inference_engine_tensor_info>& output_tensor_info);
+       void UpdateInputData(std::string layer_name, std::vector<float>& data, size_t size);
+       int Run(void);
+       void Finish();
+       int GetVectorFromOutput(std::string layer_name, std::vector<float>& feature_vec);
+       int GetOutputBuffer(std::string layer_name, inference_engine_tensor_buffer& output);
+};
+
+#endif
diff --git a/mv_machine_learning/inference/src/inference_engine_helper.cpp b/mv_machine_learning/inference/src/inference_engine_helper.cpp
new file mode 100644 (file)
index 0000000..f356f3b
--- /dev/null
@@ -0,0 +1,309 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include "inference_engine_helper.h"
+
+using namespace std;
+using namespace InferenceEngineInterface::Common;
+
+static std::map<std::string, int> Model_Formats = {
+       { "caffemodel", INFERENCE_MODEL_CAFFE }, { "pb", INFERENCE_MODEL_TF },
+       { "tflite", INFERENCE_MODEL_TFLITE }, { "t7", INFERENCE_MODEL_TORCH },
+       { "weights", INFERENCE_MODEL_DARKNET }, { "xml", INFERENCE_MODEL_DLDT },
+       { "onnx", INFERENCE_MODEL_ONNX }, {"ini", INFERENCE_MODEL_NNTRAINER }
+};
+
+InferenceEngineHelper::InferenceEngineHelper(std::string backend_name, int target_device)
+{
+       _engine = std::make_unique<InferenceEngineCommon>();
+
+       inference_engine_config config = { backend_name, -1, target_device };
+
+       int ret = _engine->BindBackend(&config);
+       if (ret != INFERENCE_ENGINE_ERROR_NONE)
+               throw std::string("fail to bind backend engine");
+
+       inference_engine_capacity capacity;
+
+       ret = _engine->GetBackendCapacity(&capacity);
+       if (ret != INFERENCE_ENGINE_ERROR_NONE)
+               throw std::string("fail to get backend engine capacity.");
+
+       ret = _engine->SetTargetDevices(config.target_devices);
+       if (ret != INFERENCE_ENGINE_ERROR_NONE)
+               throw std::string("fail to set target device.");
+}
+
+InferenceEngineHelper::~InferenceEngineHelper()
+{
+       _engine->UnbindBackend();
+}
+
+int InferenceEngineHelper::GetModelInfo(std::vector<std::string> &model_paths,
+                                                                               std::vector<std::string> &models)
+{
+       std::string model_path = model_paths[0];
+       std::string ext_str = model_path.substr(model_path.find_last_of(".") + 1);
+       std::map<std::string, int>::iterator key = Model_Formats.find(ext_str);
+       int ret = key != Model_Formats.end() ? key->second : -1;
+
+       if (ret == -1) {
+               return ret;
+       }
+
+       switch (ret) {
+       case INFERENCE_MODEL_CAFFE:
+       case INFERENCE_MODEL_TF:
+       case INFERENCE_MODEL_DARKNET:
+       case INFERENCE_MODEL_DLDT:
+       case INFERENCE_MODEL_ONNX:
+               models.push_back(model_paths[0]);
+               models.push_back(model_paths[1]);
+               break;
+       case INFERENCE_MODEL_TFLITE:
+       case INFERENCE_MODEL_TORCH:
+       case INFERENCE_MODEL_NNTRAINER:
+               models.push_back(model_paths[0]);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+int InferenceEngineHelper::PrepareTensorBuffers(InferenceEngineCommon *engine,
+                                                                                               IETensorBuffer &inputs,
+                                                                                               IETensorBuffer &outputs)
+{
+       int ret = engine->GetInputTensorBuffers(inputs);
+       if (ret != INFERENCE_ENGINE_ERROR_NONE)
+               return ret;
+
+       if (inputs.empty()) {
+               inference_engine_layer_property input_property;
+               ret = engine->GetInputLayerProperty(input_property);
+               if (ret != INFERENCE_ENGINE_ERROR_NONE)
+                       return ret;
+
+               // If backend is OpenCV then the buffers will be allocated out of this function.
+               if (input_property.layers.empty()) {
+                       return INFERENCE_ENGINE_ERROR_NONE;
+               }
+
+               for (auto& layer : input_property.layers) {
+                       inference_engine_tensor_buffer tensor_buffer;
+                       inference_engine_tensor_info tensor_info = layer.second;
+
+                       if (tensor_info.data_type == INFERENCE_TENSOR_DATA_TYPE_FLOAT32) {
+                               tensor_buffer.buffer = (void *) (new float[tensor_info.size]);
+                               tensor_buffer.size = tensor_info.size * 4;
+                       } else if (tensor_info.data_type == INFERENCE_TENSOR_DATA_TYPE_UINT8) {
+                               tensor_buffer.buffer = (void *) (new unsigned char[tensor_info.size]);
+                               tensor_buffer.size = tensor_info.size;
+                       }
+
+                       tensor_buffer.owner_is_backend = 0;
+                       tensor_buffer.data_type = tensor_info.data_type;
+                       inputs.insert(std::make_pair(layer.first, tensor_buffer));
+               }
+       }
+
+       ret = engine->GetOutputTensorBuffers(outputs);
+       if (ret != INFERENCE_ENGINE_ERROR_NONE)
+               return ret;
+
+       if (outputs.empty()) {
+               inference_engine_layer_property output_property;
+               ret = engine->GetOutputLayerProperty(output_property);
+               if (ret != INFERENCE_ENGINE_ERROR_NONE)
+                       return ret;
+
+               // If backend is OpenCV then the buffers will be allocated out of this function.
+               if (output_property.layers.empty()) {
+                       return INFERENCE_ENGINE_ERROR_NONE;
+               }
+
+               for (auto& layer : output_property.layers) {
+                       inference_engine_tensor_info tensor_info = layer.second;
+                       inference_engine_tensor_buffer tensor_buffer;
+
+                       if (tensor_info.data_type == INFERENCE_TENSOR_DATA_TYPE_FLOAT32) {
+                               tensor_buffer.buffer = (void *) (new float[tensor_info.size]);
+                               tensor_buffer.size = tensor_info.size * 4;
+                       } else if (tensor_info.data_type == INFERENCE_TENSOR_DATA_TYPE_UINT8) {
+                               tensor_buffer.buffer = (void *) (new unsigned char[tensor_info.size]);
+                               tensor_buffer.size = tensor_info.size;
+                       } else if (tensor_info.data_type == INFERENCE_TENSOR_DATA_TYPE_INT64) {
+                               tensor_buffer.buffer = (void *)(new long long[tensor_info.size]);
+                               tensor_buffer.size = tensor_info.size * 8;
+                       } else if (tensor_info.data_type == INFERENCE_TENSOR_DATA_TYPE_UINT16) {
+                               tensor_buffer.buffer = (void *)(new unsigned short[tensor_info.size]);
+                               tensor_buffer.size = tensor_info.size * 2;
+                       }
+
+                       tensor_buffer.owner_is_backend = 0;
+                       tensor_buffer.data_type = tensor_info.data_type;
+                       outputs.insert(std::make_pair(layer.first, tensor_buffer));
+               }
+       }
+
+       return INFERENCE_ENGINE_ERROR_NONE;
+}
+
+void InferenceEngineHelper::CleanupTensorBuffers(IETensorBuffer &inputs,
+                                                                                               IETensorBuffer &outputs)
+{
+       if (!inputs.empty()) {
+               for (auto& layer : inputs) {
+                       inference_engine_tensor_buffer tensor_buffer = layer.second;
+
+                       // If tensor buffer owner is a backend then skip to release the tensor buffer.
+                       // This tensor buffer will be released by the backend.
+                       if (tensor_buffer.owner_is_backend) {
+                               continue;
+                       }
+
+                       if (tensor_buffer.data_type == INFERENCE_TENSOR_DATA_TYPE_FLOAT32)
+                               delete[](float *) tensor_buffer.buffer;
+                       else
+                               delete[](unsigned char *) tensor_buffer.buffer;
+               }
+       }
+
+       if (!outputs.empty()) {
+               for (auto& layer : outputs) {
+                       inference_engine_tensor_buffer tensor_buffer = layer.second;
+
+                       // If tensor buffer owner is a backend then skip to release the tensor buffer.
+                       // This tensor buffer will be released by the backend.
+                       if (tensor_buffer.owner_is_backend) {
+                               continue;
+                       }
+
+                       if (tensor_buffer.data_type == INFERENCE_TENSOR_DATA_TYPE_FLOAT32)
+                               delete[](float *) tensor_buffer.buffer;
+                       else
+                               delete[](unsigned char *) tensor_buffer.buffer;
+               }
+       }
+}
+
+int InferenceEngineHelper::Load(std::string backbone_path)
+{
+       int ret = 0;
+
+       std::vector<std::string> models, model_paths;
+       model_paths.push_back(backbone_path);
+
+       int model_type = GetModelInfo(model_paths, models);
+
+       ret = _engine->Load(models, (inference_model_format_e) model_type);
+       if (ret != INFERENCE_ENGINE_ERROR_NONE) {
+               if (model_type != INFERENCE_MODEL_NNTRAINER)
+                       throw std::string("fail to load model file.");
+       }
+
+       ret = PrepareTensorBuffers(_engine.get(), _inputs, _outputs);
+       if (ret != INFERENCE_ENGINE_ERROR_NONE) {
+               if (model_type != INFERENCE_MODEL_NNTRAINER)
+                       throw std::string("fail to prepare tensor buffers.");
+       }
+
+       return ret;
+}
+
+int InferenceEngineHelper::UpdateLayerInfo(const std::vector<std::string>& input_layers,
+                                       const std::vector<std::string>& output_layers,
+                                       const std::vector<inference_engine_tensor_info>& input_tensor_info,
+                                       const std::vector<inference_engine_tensor_info>& output_tensor_info)
+{
+       int ret = 0;
+
+       _input_property.layers.clear();
+
+       if (input_layers.size() != input_tensor_info.size() ||
+               output_layers.size() != output_tensor_info.size())
+               throw std::string("layer name and tensor info size are different.");
+
+       for (size_t idx = 0; idx < input_layers.size(); ++idx)
+               _input_property.layers.insert(std::make_pair(input_layers[idx], input_tensor_info[idx]));
+
+       ret = _engine->SetInputLayerProperty(_input_property);
+       if (ret != INFERENCE_ENGINE_ERROR_NONE)
+               throw std::string("fail to set input layer property.");
+
+       _output_property.layers.clear();
+
+       for (size_t idx = 0; idx < output_layers.size(); ++idx)
+               _output_property.layers.insert(std::make_pair(output_layers[idx], output_tensor_info[idx]));
+
+       ret = _engine->SetOutputLayerProperty(_output_property);
+       if (ret != INFERENCE_ENGINE_ERROR_NONE)
+               throw std::string("fail to set output layer property.");
+
+       return ret;
+}
+
+void InferenceEngineHelper::UpdateInputData(std::string layer_name, std::vector<float>& data, size_t size)
+{
+       if (data.size() * 4 != size)
+               throw std::string("number of bytes of data should be same as size");
+
+       auto it = _inputs.find(layer_name);
+
+       if (it == _inputs.end())
+               throw std::string("Invalid layer name.");
+
+       memcpy(it->second.buffer, data.data(), size);
+}
+
+int InferenceEngineHelper::Run(void)
+{
+       return _engine->Run(_inputs, _outputs);
+}
+
+void InferenceEngineHelper::Finish()
+{
+       _engine->UnbindBackend();
+}
+
+int InferenceEngineHelper::GetVectorFromOutput(std::string layer_name, std::vector<float>& feature_vec)
+{
+       auto it = _outputs.find(layer_name);
+       if (it != _outputs.end()) {
+               inference_engine_tensor_buffer& buffer = it->second;
+               float *data = static_cast<float *>(buffer.buffer);
+
+               for (size_t idx = 0; idx < buffer.size / 4; ++idx)
+                       feature_vec.push_back(data[idx]);
+
+               return INFERENCE_ENGINE_ERROR_NONE;
+       }
+
+       return INFERENCE_ENGINE_ERROR_INTERNAL;
+}
+
+int InferenceEngineHelper::GetOutputBuffer(std::string layer_name, inference_engine_tensor_buffer& output)
+{
+       auto it = _outputs.find(layer_name);
+       if (it != _outputs.end()) {
+               output = it->second;
+               return INFERENCE_ENGINE_ERROR_NONE;
+       }
+
+       return INFERENCE_ENGINE_ERROR_INTERNAL;
+}
\ No newline at end of file
diff --git a/mv_machine_learning/training/CMakeLists.txt b/mv_machine_learning/training/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6ecd656
--- /dev/null
@@ -0,0 +1,21 @@
+project(${MV_TRAINING_LIB_NAME})
+cmake_minimum_required(VERSION 2.6)
+
+pkg_check_modules(${PROJECT_NAME}_DEP REQUIRED training-engine-interface-common)
+file(GLOB MV_TRAINING_SOURCE_LIST  "${PROJECT_SOURCE_DIR}/src/*.c" "${PROJECT_SOURCE_DIR}/src/*.cpp")
+
+find_package(OpenCV REQUIRED dnn imgproc)
+if(NOT OpenCV_FOUND)
+       message(SEND_ERROR "OpenCV NOT FOUND")
+       return()
+endif()
+
+if(FORCED_STATIC_BUILD)
+       add_library(${PROJECT_NAME} STATIC ${MV_TRAINING_SOURCE_LIST})
+else()
+       add_library(${PROJECT_NAME} SHARED ${MV_TRAINING_SOURCE_LIST})
+endif()
+
+target_link_libraries(${PROJECT_NAME} ${MV_COMMON_LIB_NAME} ${OpenCV_LIBS} ${${PROJECT_NAME}_DEP_LIBRARIES})
+target_include_directories(${PROJECT_NAME} PRIVATE include)
+install(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR})
diff --git a/mv_machine_learning/training/include/data_set_manager.h b/mv_machine_learning/training/include/data_set_manager.h
new file mode 100644 (file)
index 0000000..300fb6c
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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 DATA_SET_MANAGER_H
+#define DATA_SET_MANAGER_H
+
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+#include "feature_vector_manager.h"
+
+class DataSetManager {
+protected:
+       std::vector<std::vector<float>> _data;
+       std::vector<std::vector<float>> _labels;
+       std::vector<unsigned int> _label_index;
+       size_t _feature_vector_size;
+       size_t _label_size;
+       size_t _data_set_length;
+
+public:
+       DataSetManager();
+       ~DataSetManager();
+
+       virtual void LoadDataSet(const std::string file_name);
+       void Clear();
+       bool IsFeatureVectorDuplicated(const std::vector<float>& vec);
+       std::vector<std::vector<float>>& GetData(void);
+       std::vector<std::vector<float>>& GetLabel(void);
+       size_t GetFeaVecSize(void);
+       size_t GetLabelSize(void);
+       size_t GetDataSetLen(void);
+       std::vector<unsigned int>& GetLabelIdx(void);
+};
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/training/include/feature_vector_manager.h b/mv_machine_learning/training/include/feature_vector_manager.h
new file mode 100644 (file)
index 0000000..3daf714
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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 FEATURE_VECTOR_MANAGER_H
+#define FEATURE_VECTOR_MANAGER_H
+
+#include <string.h>
+#include <iostream>
+#include <fstream>
+#include <istream>
+#include <algorithm>
+#include <vector>
+#include <map>
+
+#include <opencv2/opencv.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+
+#include "file_util.h"
+
+static const unsigned int feature_vector_signagure = 0xFEA09841;
+
+typedef struct {
+       unsigned int signature;
+       size_t feature_size;
+       size_t one_hot_table_size;
+       unsigned int data_set_cnt;
+} FeaVecHeader;
+
+class FeatureVectorManager {
+protected:
+       std::string _feature_vector_file;
+
+public:
+       FeatureVectorManager(const std::string feature_vector_file = "feature_vector_file.dat");
+       ~FeatureVectorManager();
+
+       const std::string& GetFileName();
+       static void GetVecFromImg(const std::string image_file, std::vector<float>& vec, int width, int height);
+       static void GetVecFromRGB(unsigned char *in_data, std::vector<float>& vec, int width, int height,
+                                                         int re_width, int re_height);
+       static void GetVecFromXRGB(unsigned char *in_data, std::vector<float>& vec,
+                                                          int in_width, int in_height, int re_width, int re_height);
+
+       virtual void WriteHeader(size_t feature_size, size_t one_hot_table_size, unsigned int  data_set_cnt);
+       virtual void ReadHeader(FeaVecHeader& header);
+       virtual void WriteFeatureVec(std::vector<float>& feature_vec, const int max_label, const int label_index);
+};
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/training/include/file_util.h b/mv_machine_learning/training/include/file_util.h
new file mode 100644 (file)
index 0000000..5342951
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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 FILE_UTIL_H
+#define FILE_UTIL_H
+
+#include <iostream>
+#include <sys/stat.h>
+
+namespace FaceRecogUtil
+{
+    bool IsFileExist(const std::string file_path);
+    bool IsImageFile(const std::string image_file);
+}
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/training/include/label_manager.h b/mv_machine_learning/training/include/label_manager.h
new file mode 100644 (file)
index 0000000..8389ed4
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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 LABEL_MANAGER_H
+#define LABEL_MANAGER_H
+
+#include <string.h>
+#include <iostream>
+#include <fstream>
+#include <istream>
+#include <algorithm>
+#include <vector>
+#include <map>
+
+#include "file_util.h"
+
+class LabelManager {
+private:
+       std::map<std::string, std::string> _labels_and_files;
+       std::string _label_file;
+       // Correct answer should be bigger than this value. In defualt it has -0.67.
+       static constexpr float _default_decision_threshold = -0.97;
+       float _decision_threshold;
+       static constexpr float _decision_weight = 1.2;
+
+public:
+       LabelManager(std::string label_file = "labels.dat");
+       ~LabelManager();
+       void Clear();
+       float GetDecisionThreshold();
+       float GetDecisionWeight();
+       unsigned int GetLabelIndex(const std::string given_label);
+       bool IsExist(const std::string given_label);
+       unsigned int RemoveLabel(const std::string given_label);
+       int GetLabelString(std::string& label, const int idx);
+       unsigned int AddLabelToFile(std::string given_label);
+       int ImportLabel(void);
+       bool AddLabelToMap(const std::string given_label, const std::string image_file);
+       int GetMaxLabel(const std::string label_file);
+       int GetMaxLabel();
+       std::string GetLabelFromAnswer(const std::vector<float>& result);
+};
+
+#endif
diff --git a/mv_machine_learning/training/include/training_model.h b/mv_machine_learning/training/include/training_model.h
new file mode 100644 (file)
index 0000000..7c2373c
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics 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 TRAINING_MODEL_H
+#define TRAINING_MODEL_H
+
+#include <iostream>
+#include <map>
+
+#include "training_engine_error.h"
+#include "training_engine_common_impl.h"
+#include "inference_engine_common_impl.h"
+#include "data_set_manager.h"
+#include "feature_vector_manager.h"
+
+typedef struct {
+       std::string backend_name;
+       unsigned int target_device;
+       std::vector<std::string> input_layer_names;
+       std::vector<std::string> output_layer_names;
+       std::vector<inference_engine_tensor_info> input_tensor_info;
+       std::vector<inference_engine_tensor_info> output_tensor_info;
+       training_engine_optimizer_property optimizer_property;
+       training_engine_compile_property compile_property;
+} TrainingEngineBackendInfo;
+
+class TrainingModel {
+protected:
+       std::unique_ptr<TrainingEngineInterface::Common::TrainingEngineCommon> _training;
+       std::unique_ptr<training_engine_model> _model;
+       std::unique_ptr<training_engine_dataset> _data_set;
+       std::map<int, std::string> _backend_lookup;
+       std::string _internal_model_file;
+
+public:
+       TrainingModel(const training_engine_backend_type_e backend_type = TRAINING_ENGINE_BACKEND_NNTRAINER,
+                                 const std::string internal_model_file = "model_and_weights.ini");
+       ~ TrainingModel();
+
+       void ApplyDataSet(std::unique_ptr<DataSetManager>& data_set);
+       void Compile();
+       void Training();
+
+       virtual void ConfigureModel(int num_of_class);
+       virtual TrainingEngineBackendInfo& GetTrainingEngineInfo();
+       virtual void SaveModel(const std::string file_path);
+};
+
+#endif
\ No newline at end of file
diff --git a/mv_machine_learning/training/src/data_set_manager.cpp b/mv_machine_learning/training/src/data_set_manager.cpp
new file mode 100644 (file)
index 0000000..c1e72dc
--- /dev/null
@@ -0,0 +1,105 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include "data_set_manager.h"
+
+using namespace std;
+
+DataSetManager::DataSetManager() : _data(), _labels(), _label_index(), _feature_vector_size(), _label_size(), _data_set_length()
+{
+
+}
+
+DataSetManager::~DataSetManager()
+{
+       Clear();
+}
+
+void DataSetManager::LoadDataSet(const std::string file_name)
+{
+       LoadDataSet(file_name);
+}
+
+void DataSetManager::Clear()
+{
+       for (auto& data : _data)
+               data.clear();
+
+       _data.clear();
+
+       for (auto& label : _labels)
+               label.clear();
+
+       _labels.clear();
+       _label_index.clear();
+}
+
+bool DataSetManager::IsFeatureVectorDuplicated(const std::vector<float>& vec)
+{
+       if (_data.empty())
+               return false;
+
+       bool duplicated = false;
+
+       for (size_t item_idx = 0; item_idx < _data.size(); ++item_idx) {
+               if (vec.size() != _data[item_idx].size())
+                       continue;
+
+               duplicated = true;
+
+               for (size_t val_idx = 0; val_idx < _data[item_idx].size(); ++val_idx) {
+                       if (vec[val_idx] != _data[item_idx][val_idx]) {
+                               duplicated = false;
+                               break;
+                       }
+               }
+
+               if (duplicated)
+                       return true;
+       }
+
+       return duplicated;
+}
+
+std::vector<std::vector<float>>& DataSetManager::GetData(void)
+{
+       return _data;
+}
+
+std::vector<std::vector<float>>& DataSetManager::GetLabel(void)
+{
+       return _labels;
+}
+
+size_t DataSetManager::GetFeaVecSize(void)
+{
+       return _feature_vector_size;
+}
+
+size_t DataSetManager::GetLabelSize(void)
+{
+       return _label_size;
+}
+
+size_t DataSetManager::GetDataSetLen(void)
+{
+       return _data_set_length;
+}
+
+std::vector<unsigned int>& DataSetManager::GetLabelIdx(void)
+{
+       return _label_index;
+}
\ No newline at end of file
diff --git a/mv_machine_learning/training/src/feature_vector_manager.cpp b/mv_machine_learning/training/src/feature_vector_manager.cpp
new file mode 100644 (file)
index 0000000..663cf70
--- /dev/null
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include <opencv2/opencv.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+
+#include "feature_vector_manager.h"
+
+using namespace std;
+
+FeatureVectorManager::FeatureVectorManager(const std::string feature_vector_file) : _feature_vector_file(feature_vector_file)
+{
+
+}
+
+FeatureVectorManager::~FeatureVectorManager()
+{
+
+}
+
+const std::string& FeatureVectorManager::GetFileName() {
+       return _feature_vector_file;
+}
+
+void FeatureVectorManager::GetVecFromImg(const std::string image_file, std::vector<float>& vec, int width, int height)
+{
+       cv::Mat src, dst;
+
+       if (!FaceRecogUtil::IsImageFile(image_file))
+               throw std::string("Invalid image file.");
+
+       src = cv::imread(image_file);
+
+       if (!src.data)
+               throw std::string("Invalid src.data.");
+
+       cv::cvtColor(src, src, cv::COLOR_BGR2RGB);
+
+       cv::Mat resized;
+
+       resize(src, resized, cv::Size(width, height), 0, 0, cv::INTER_CUBIC);
+
+       cv::Mat floatSrc;
+
+       resized.convertTo(floatSrc, CV_32FC3);
+
+       cv::Mat meaned = cv::Mat(floatSrc.size(), CV_32FC3, cv::Scalar(127.5f, 127.5f, 127.5f));
+
+       cv::subtract(floatSrc, meaned, dst);
+       dst /= 127.5f;
+
+       vec.assign((float *)dst.data, (float *)dst.data + dst.total() * dst.channels());
+}
+
+void FeatureVectorManager::GetVecFromRGB(unsigned char *in_data, std::vector<float>& vec,
+                                                                                int width, int height, int re_width, int re_height)
+{
+       cv::Mat cvSrc = cv::Mat(cv::Size(width, height), CV_MAKETYPE(CV_8U, 3), in_data).clone();
+
+       cv::Mat resized;
+
+       resize(cvSrc, resized, cv::Size(re_width, re_height), 0, 0, cv::INTER_CUBIC);
+
+       cv::Mat floatSrc;
+
+       resized.convertTo(floatSrc, CV_32FC3);
+
+       cv::Mat meaned = cv::Mat(floatSrc.size(), CV_32FC3, cv::Scalar(127.5f, 127.5f, 127.5f));
+       cv::Mat dst;
+
+       cv::subtract(floatSrc, meaned, dst);
+       dst /= 127.5f;
+
+       vec.assign((float *)dst.data, (float *)dst.data + dst.total() * dst.channels());
+}
+
+void FeatureVectorManager::GetVecFromXRGB(unsigned char *in_data, std::vector<float>& vec,
+                                                       int in_width, int in_height, int re_width, int re_height)
+{
+       cv::Mat argb(cv::Size(in_width, in_height), CV_8UC4, in_data);
+
+       cv::Mat split_rgbx[4];
+       cv::split (argb, split_rgbx);
+       cv::Mat splitted[] = {split_rgbx[0], split_rgbx[1], split_rgbx[2]};
+       cv::Mat rgb;
+       cv::merge(splitted, 3, rgb);
+
+       cv::Mat resized;
+
+       resize(rgb, resized, cv::Size(re_width, re_height), 0, 0, cv::INTER_CUBIC);
+
+       cv::Mat floatSrc;
+
+       resized.convertTo(floatSrc, CV_32FC3);
+
+       cv::Mat meaned = cv::Mat(floatSrc.size(), CV_32FC3, cv::Scalar(127.5f, 127.5f, 127.5f));
+
+       cv::Mat dst;
+
+       cv::subtract(floatSrc, meaned, dst);
+       dst /= 127.5f;
+
+       vec.assign((float *)dst.data, (float *)dst.data + dst.total() * dst.channels());
+}
+
+void FeatureVectorManager::WriteHeader(size_t feature_size, size_t one_hot_table_size, unsigned int  data_set_cnt)
+{
+       WriteHeader(feature_size, one_hot_table_size, data_set_cnt);
+}
+
+void FeatureVectorManager::ReadHeader(FeaVecHeader& header)
+{
+       ReadHeader(header);
+}
+
+void FeatureVectorManager::WriteFeatureVec(std::vector<float>& feature_vec, const int max_label, const int label_index)
+{
+       WriteFeatureVec(feature_vec, max_label, label_index);
+}
\ No newline at end of file
diff --git a/mv_machine_learning/training/src/file_util.cpp b/mv_machine_learning/training/src/file_util.cpp
new file mode 100644 (file)
index 0000000..32f7f8e
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include <file_util.h>
+
+namespace FaceRecogUtil
+{
+    bool IsFileExist(const std::string file_path)
+    {
+        struct stat fileStat;
+
+        if (stat(file_path.c_str(), &fileStat))
+            return false;
+
+        if (!S_ISREG(fileStat.st_mode))
+            return false;
+
+        return true;
+    }
+
+    bool IsImageFile(const std::string image_file)
+       {
+
+               size_t size = image_file.size();
+
+               // At least, the length of a image file name should be more then 5. i.e., a.bmp, a.jpg, a.png, ...
+               if (size < 5)
+                       return false;
+
+               std::string ext = image_file.substr(size - 3);
+               if (ext.compare("bmp") != 0 && ext.compare("jpg") != 0 && ext.compare("png") != 0)
+                       return false;
+
+               return true;
+       }
+}
\ No newline at end of file
diff --git a/mv_machine_learning/training/src/label_manager.cpp b/mv_machine_learning/training/src/label_manager.cpp
new file mode 100644 (file)
index 0000000..13a76ec
--- /dev/null
@@ -0,0 +1,287 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include <dlog.h>
+#include <mv_private.h>
+
+#include "label_manager.h"
+
+#define DECISION_THRESHOLD_FILE                "decision.txt"
+
+using namespace std;
+
+LabelManager::LabelManager(std::string label_file) : _labels_and_files(), _label_file(label_file)
+{
+       std::ifstream readFile;
+
+       readFile.open(DECISION_THRESHOLD_FILE);
+
+       if (readFile.fail())
+               _decision_threshold = _default_decision_threshold;
+       else
+               readFile >> _decision_threshold;
+
+       LOGD("decision_threshold value is %f", _decision_threshold);
+}
+
+LabelManager::~LabelManager() { }
+
+void LabelManager::Clear() {
+       _labels_and_files.clear();
+}
+
+float LabelManager::GetDecisionThreshold() {
+       return _decision_threshold;
+}
+
+float LabelManager::GetDecisionWeight() {
+       return _decision_weight;
+}
+
+unsigned int LabelManager::GetLabelIndex(const std::string given_label)
+{
+       std::ifstream readFile;
+
+       readFile.open(_label_file.c_str());
+
+       int label_index = -1;
+
+       if (readFile.fail())
+               throw std::string("Fail to open " + _label_file + " file.");
+
+       std::string line;
+
+       while (getline(readFile, line)) {
+               label_index++;
+
+               if (line.compare(given_label) == 0) {
+                       readFile.close();
+
+                       return label_index;
+               }
+       }
+
+       readFile.close();
+       throw std::string("Label index not found.");
+}
+
+bool LabelManager::IsExist(const std::string given_label)
+{
+       std::ifstream readFile;
+
+       readFile.open(_label_file.c_str());
+
+       int label_index = -1;
+
+       if (readFile.fail())
+               throw std::string("Fail to open " + _label_file + " file.");
+
+       std::string line;
+
+       while (getline(readFile, line)) {
+               label_index++;
+
+               if (line.compare(given_label) == 0) {
+                       readFile.close();
+                       return true;
+               }
+       }
+
+       readFile.close();
+       return false;
+}
+
+unsigned int LabelManager::RemoveLabel(const std::string given_label)
+{
+       std::ifstream readFile;
+
+       readFile.open(_label_file.c_str());
+
+       std::ofstream writeFile;
+
+       std::string new_label_file(_label_file);
+
+       new_label_file += ".new";
+
+       writeFile.open(new_label_file.c_str(), std::ios::out | std::ios::binary);
+
+       int label_index = 0;
+
+       if (readFile.fail() || !writeFile.is_open())
+               throw std::string("Fail to open " + _label_file + "or " + new_label_file + " file.");
+
+       std::string line;
+
+       while (getline(readFile, line)) {
+               if (line.compare(given_label) != 0) {
+                       line += "\n";
+                       writeFile.write(line.c_str(), line.size());
+                       label_index++;
+               }
+       }
+
+       readFile.close();
+       writeFile.close();
+
+       if (label_index == 0) {
+               // Just keep original version in case of no copy so drop the new label file.
+               remove(new_label_file.c_str());
+       } else {
+               remove(_label_file.c_str());
+               rename(new_label_file.c_str(), _label_file.c_str());
+       }
+
+       return label_index;
+}
+
+int LabelManager::GetLabelString(std::string& label, const int idx)
+{
+       std::ifstream readFile;
+
+       readFile.open(_label_file.c_str());
+
+       int label_index = -1;
+       int ret = 0;
+
+       if (readFile.fail())
+               throw std::string("Fail to open " + _label_file + " file.");
+
+       std::string line;
+
+       while (getline(readFile, line)) {
+               label_index++;
+               line += "\n";
+
+               if (idx == label_index) {
+                       label = line;
+                       label.erase(std::remove(label.begin(), label.end(), '\n'), label.end());
+                       break;
+               }
+       }
+
+       readFile.close();
+       return ret;
+}
+
+unsigned int LabelManager::AddLabelToFile(std::string given_label)
+{
+       std::ofstream writeFile;
+
+       writeFile.open(_label_file.c_str(), std::ios::out | std::ios::app);
+
+       if (!writeFile.is_open())
+               throw std::string("Fail to open " + _label_file + " file.");
+
+       given_label += "\n";
+       writeFile.write(given_label.c_str(), given_label.size());
+       writeFile.close();
+
+       return GetMaxLabel(_label_file);
+}
+
+int LabelManager::ImportLabel(void)
+{
+       // label count is 0 if lael file doesn't exist.
+       if (!FaceRecogUtil::IsFileExist(_label_file))
+               return 0;
+
+       std::ifstream readFile;
+
+       readFile.open(_label_file);
+
+       int label_cnt = 0;
+
+       if (readFile.fail())
+               throw std::string("Fail to open " + _label_file + " file.");
+
+       std::string line;
+
+       while (getline(readFile, line)) {
+               bool duplicated = AddLabelToMap(line, line);
+               if (duplicated)
+                       continue;
+
+               label_cnt++;
+       }
+
+       readFile.close();
+
+       return label_cnt;
+}
+
+
+bool LabelManager::AddLabelToMap(const std::string given_label, const std::string image_file)
+{
+       // Find same one if not empty. If same one exists in the map then skip.
+       if (!_labels_and_files.empty()) {
+               auto item = _labels_and_files.find(given_label);
+               if (item != _labels_and_files.end())
+                       return true;
+       }
+
+       _labels_and_files.insert(std::pair<std::string, std::string>(given_label, image_file));
+
+       return false;
+}
+
+int LabelManager::GetMaxLabel(const std::string label_file)
+{
+
+       // label count is 0 if lael file doesn't exist.
+       if (!FaceRecogUtil::IsFileExist(label_file))
+               return 0;
+
+       std::ifstream readFile;
+
+       readFile.open(label_file.c_str());
+
+       int label_cnt = 0;
+
+       if (readFile.fail())
+               throw std::string("Fail to open " + label_file + " file.");
+
+       std::string line;
+
+       while (getline(readFile, line))
+               label_cnt++;
+
+       readFile.close();
+
+       return label_cnt;
+}
+
+int LabelManager::GetMaxLabel()
+{
+       return GetMaxLabel(_label_file);
+}
+
+std::string LabelManager::GetLabelFromAnswer(const std::vector<float>& result)
+{
+       if (result.empty())
+               throw std::string("result vector is empty.");
+
+       int answer_idx = max_element(result.begin(), result.end()) - result.begin();
+       if (result[answer_idx] < _decision_threshold)
+               return std::string("Not recognized.");
+
+       std::string answer_label;
+
+       int ret = GetLabelString(answer_label, answer_idx);
+       if (ret)
+               throw std::string("answer label not found.");
+
+       return answer_label;
+}
\ No newline at end of file
diff --git a/mv_machine_learning/training/src/training_model.cpp b/mv_machine_learning/training/src/training_model.cpp
new file mode 100644 (file)
index 0000000..c362338
--- /dev/null
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include <string.h>
+#include <fstream>
+#include <istream>
+#include <tuple>
+#include <map>
+#include <algorithm>
+
+#include <sys/stat.h>
+
+#include <dlog.h>
+#include <mv_private.h>
+
+#include "training_model.h"
+
+using namespace std;
+using namespace TrainingEngineInterface::Common;
+
+TrainingModel::TrainingModel(const training_engine_backend_type_e backend_type, const std::string internal_model_file)
+{
+       _internal_model_file = internal_model_file;
+
+       _backend_lookup.insert(make_pair<int, string>(TRAINING_ENGINE_BACKEND_NNTRAINER, "nntrainer"));
+
+       try {
+               _training = std::make_unique<TrainingEngineInterface::Common::TrainingEngineCommon>();
+       } catch (std::string& exception) {
+               std::cerr << exception << std::endl;
+               return;
+       } catch (const std::exception& e) {
+               std::cerr << e.what() << std::endl;
+               return;
+       }
+}
+TrainingModel::~ TrainingModel()
+{
+       if (_training)
+               _training->UnbindBackend();
+}
+
+void TrainingModel::ApplyDataSet(unique_ptr<DataSetManager>& data_set)
+{
+       vector<vector<float>> values = data_set->GetData();
+       vector<vector<float>> labels = data_set->GetLabel();
+
+       LOGD("Generating feature vectors for training");
+
+       _data_set = _training->CreateDataset();
+       if (!_data_set)
+               throw std::string("Fail to create a dataset.");
+
+       int ret;
+
+       for (size_t idx = 0; idx < values.size(); ++idx) {
+               ret = _training->AddDataToDataset(_data_set.get(), values[idx], labels[idx], TRAINING_DATASET_TYPE_TRAIN);
+               if (ret != TRAINING_ENGINE_ERROR_NONE)
+                       throw string("Fail to add data to dataset.");
+       }
+
+       data_set->Clear();
+
+       ret = _training->SetDataset(_model.get(), _data_set.get());
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw string("Fail to set dataset to model.");
+}
+
+void TrainingModel::Compile()
+{
+       auto optimizer = _training->CreateOptimizer(TRAINING_OPTIMIZER_TYPE_SGD);
+       if (!optimizer)
+               throw string("Fail to create a optimizer.");
+
+       int ret = _training->SetOptimizerProperty(optimizer.get(), GetTrainingEngineInfo().optimizer_property);
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw string("Fail to set optimizer property.");
+
+       ret = _training->AddOptimizer(_model.get(), optimizer.get());
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw string("Fail to add optimizer to model.");
+
+       ret = _training->CompileModel(_model.get(), GetTrainingEngineInfo().compile_property);
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw string("Fail to compile model.");
+}
+
+void TrainingModel::Training()
+{
+       training_engine_model_property model_property;
+       int ret = _training->TrainModel(_model.get(), model_property);
+       if (ret != TRAINING_ENGINE_ERROR_NONE)
+               throw string("Fail to train model.");
+
+       try {
+               // Save model file.
+               SaveModel(_internal_model_file);
+       } catch (const string& exception) {
+               throw exception;
+       } catch (const exception& e) {
+               throw e;
+       }
+}
+
+void TrainingModel::ConfigureModel(int num_of_class)
+{
+       // Child class implements model and layer configuration for training approach
+       // because there are various apparches so call child class's function.
+       ConfigureModel(num_of_class);
+}
+
+TrainingEngineBackendInfo& TrainingModel::GetTrainingEngineInfo()
+{
+       return GetTrainingEngineInfo();
+}
+
+void TrainingModel::SaveModel(const std::string file_path)
+{
+       SaveModel(file_path);
+}
\ No newline at end of file
index 9ea58c3..3fc9db1 100644 (file)
@@ -1,6 +1,6 @@
 Name:        capi-media-vision
 Summary:     Media Vision library for Tizen Native API
-Version:     0.16.0
+Version:     0.17.0
 Release:     0
 Group:       Multimedia/Framework
 License:     Apache-2.0 and BSD-3-Clause
@@ -13,6 +13,7 @@ BuildRequires: pkgconfig(opencv)
 BuildRequires: pkgconfig(json-glib-1.0)
 BuildRequires: pkgconfig(iniparser)
 BuildRequires: pkgconfig(inference-engine-interface-common)
+BuildRequires: pkgconfig(training-engine-interface-common)
 %if !0%{?ml_only:1}
 BuildRequires: pkgconfig(glib-2.0)
 BuildRequires: pkgconfig(zbar)
@@ -302,6 +303,8 @@ install -m 0644 gcov-obj/* %{buildroot}%{_datadir}/gcov/obj/%{name}
 %manifest %{name}.manifest
 %license LICENSE.APLv2
 %{_libdir}/libmv_inference*.so
+%{_libdir}/libmv_training.so
+%{_libdir}/libmv_face_recognition.so
 
 %files machine_learning-devel
 %{_includedir}/media/mv_infer*.h
index 57785be..14ddb41 100644 (file)
@@ -2,3 +2,4 @@ project(machine_learning)
 cmake_minimum_required(VERSION 2.6)
 
 add_subdirectory(${PROJECT_SOURCE_DIR}/inference)
+add_subdirectory(${PROJECT_SOURCE_DIR}/face_recognition)
\ No newline at end of file
diff --git a/test/testsuites/machine_learning/face_recognition/CMakeLists.txt b/test/testsuites/machine_learning/face_recognition/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f6c71f2
--- /dev/null
@@ -0,0 +1,11 @@
+project(mv_face_recognition_test_suite)
+cmake_minimum_required(VERSION 2.6)
+
+add_executable(${PROJECT_NAME} test_face_recognition.cpp)
+
+target_link_libraries(${PROJECT_NAME} gtest gtest_main
+                                                                         mv_face_recognition
+                                      mv_image_helper
+)
+
+install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/test/testsuites/machine_learning/face_recognition/test_face_recognition.cpp b/test/testsuites/machine_learning/face_recognition/test_face_recognition.cpp
new file mode 100644 (file)
index 0000000..b92e4ca
--- /dev/null
@@ -0,0 +1,216 @@
+/**
+ * Copyright (c) 2021 Samsung Electronics 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.
+ */
+
+#include <iostream>
+#include <string.h>
+#include <map>
+
+#include "gtest/gtest.h"
+
+#include "ImageHelper.h"
+#include "mv_face_recognition.h"
+
+using namespace testing;
+using namespace std;
+
+static const map<string, string> training_images = {
+       { "037830.png", "2929" },
+       { "038965.png", "2929" },
+       { "045978.png", "2929" },
+       { "050501.png", "2929" },
+       { "065899.png", "2929" },
+       { "010348.png", "7779" },
+       { "029342.png", "7779" },
+       { "035939.png", "7779" },
+       { "061310.png", "7779" },
+       { "062261.png", "7779" },
+       { "000928.png", "3448" },
+       { "008922.png", "3448" },
+       { "029633.png", "3448" },
+       { "032962.png", "3448" },
+       { "054616.png", "3448" }
+};
+
+static const map<string, string> test_images = {
+       { "068468.png", "2929" },
+       { "068883.png", "2929" },
+       { "075004.png", "2929" },
+       { "078125.png", "2929" },
+       { "080649.png", "2929" },
+       { "074645.png", "7779" },
+       { "086536.png", "7779" },
+       { "089334.png", "7779" },
+       { "096514.png", "7779" },
+       { "100336.png", "7779" },
+       { "054757.png", "3448" },
+       { "064838.png", "3448" },
+       { "072749.png", "3448" },
+       { "073526.png", "3448" },
+       { "080451.png", "3448" }
+};
+
+using namespace MediaVision::Common;
+
+TEST(FaceRecognitionTest, FaceRecognitionClassShouldBeOk)
+{
+       mv_face_recognition_h handle;
+       vector<string> answers = {
+               "3448", "3448", "2929", "2929", "3448",
+               "3448", "7779", "2929", "2929", "3448",
+               "2929", "7779", "7779", "7779", "7779"
+       };
+
+       int ret = mv_face_recognition_create(&handle);
+       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+       ret = mv_face_recognition_prepare(handle);
+       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+       for (auto& image : training_images) {
+               const string image_path = string("/opt/usr/images/training/") + image.first;
+               mv_source_h mv_source = NULL;
+
+               int ret = mv_create_source(&mv_source);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               ret = ImageHelper::loadImageToSource(image_path.c_str(), mv_source);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               ret = mv_face_recognition_register(handle, mv_source, image.second.c_str());
+               ASSERT_EQ(ret, 0);
+
+               ret = mv_destroy_source(mv_source);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+       }
+
+       unsigned int image_idx = 0;
+
+       for (auto& image : test_images) {
+               const string image_path = string("/opt/usr/images/test/") + image.first;
+               mv_source_h mv_source = NULL;
+
+               int ret = mv_create_source(&mv_source);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               ret = ImageHelper::loadImageToSource(image_path.c_str(), mv_source);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               char out_label[MAX_LABEL_SIZE];
+
+               ret = mv_face_recognition_inference(handle, mv_source, out_label);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               ret = mv_destroy_source(mv_source);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               ASSERT_TRUE(answers[image_idx++] == string(out_label));
+       }
+
+       ret = mv_face_recognition_destroy(handle);
+       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+}
+
+TEST(FaceRecognitionTest, FaceRecognitionClassWithEachLabelRemovalShouldBeOk)
+{
+       vector<string> labels = { "", "3448", "2929", "7779" };
+       vector<vector<string>> answers = {
+                       { "3448", "3448", "2929", "2929", "3448",
+                       "3448", "7779", "2929", "2929", "3448",
+                       "2929", "7779", "7779", "7779", "7779" },
+                       { "7779", "7779", "2929", "2929", "7779",
+                       "2929", "7779", "2929", "2929", "7779",
+                       "2929", "7779", "7779", "7779", "7779" },
+                       { "7779", "3448", "none", "none", "3448",
+                       "3448", "7779", "none", "none", "3448",
+                       "none", "7779", "7779", "7779", "7779" },
+                       { "3448", "3448", "2929", "2929", "3448",
+                       "3448", "none", "2929", "2929", "3448",
+                       "2929", "none", "none", "none", "none" }
+               };
+
+       unsigned int label_idx = 0;
+
+       for (auto& label : labels) {
+               mv_face_recognition_h handle;
+
+               int ret = mv_face_recognition_create(&handle);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               ret = mv_face_recognition_prepare(handle);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+               auto& answer = answers[label_idx++];
+               unsigned int image_idx = 0;
+
+               for (auto& image : training_images) {
+                       const string image_path = string("/opt/usr/images/training/") + image.first;
+                       mv_source_h mv_source = NULL;
+
+                       ret = mv_create_source(&mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       ret = ImageHelper::loadImageToSource(image_path.c_str(), mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       ret = mv_face_recognition_register(handle, mv_source, image.second.c_str());
+                       ASSERT_EQ(ret, 0);
+
+                       ret = mv_destroy_source(mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+               }
+
+
+               if (!label.empty()) {
+                       ret = mv_face_recognition_unregister(handle, label.c_str());
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+               }
+
+               for (auto& image : test_images) {
+                       const string image_path = string("/opt/usr/images/test/") + image.first;
+                       mv_source_h mv_source = NULL;
+
+                       int ret = mv_create_source(&mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       ret = ImageHelper::loadImageToSource(image_path.c_str(), mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       char out_label[MAX_LABEL_SIZE];
+
+                       ret = mv_face_recognition_inference(handle, mv_source, out_label);
+                       if (ret == MEDIA_VISION_ERROR_NO_DATA) {
+                               strcpy(out_label, "none");
+                               ret = MEDIA_VISION_ERROR_NONE;
+                       }
+
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       ret = mv_destroy_source(mv_source);
+                       ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+
+                       ASSERT_TRUE(answer[image_idx++] == string(out_label));
+               }
+
+               ret = mv_face_recognition_destroy(handle);
+               ASSERT_EQ(ret, MEDIA_VISION_ERROR_NONE);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+}