diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/docker_start.sh b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/docker_start.sh index 944bca3cdac8e3f2d47ceb0e2b6eb181a405de11..eff8f5260602cd3ed58ee589d4123e4048dedd50 100644 --- a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/docker_start.sh +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/docker_start.sh @@ -1,10 +1,23 @@ #!/bin/bash +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License.mitations under the License. docker_image=$1 data_dir=$2 model_dir=$3 -docker run -it --ipc=host \ +docker run -it -u root --ipc=host \ --device=/dev/davinci0 \ --device=/dev/davinci1 \ --device=/dev/davinci2 \ @@ -19,7 +32,4 @@ docker run -it --ipc=host \ -v /usr/local/Ascend/add-ons/:/usr/local/Ascend/add-ons/ \ -v ${model_dir}:${model_dir} \ -v ${data_dir}:${data_dir} \ - -v /var/log/npu/conf/slog/slog.conf:/var/log/npu/conf/slog/slog.conf \ - -v /var/log/npu/slog/:/var/log/npu/slog -v /var/log/npu/profiling/:/var/log/npu/profiling \ - -v /var/log/npu/dump/:/var/log/npu/dump -v /var/log/npu/:/usr/slog ${docker_image} \ - /bin/bash \ No newline at end of file + -v /root/ascend/log:/root/ascend/log ${docker_image} /bin/bash diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/config/hourglass.pipeline b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/config/hourglass.pipeline new file mode 100644 index 0000000000000000000000000000000000000000..12844598c9cc5dabfe78bd49cb16ba833d24bc8b --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/config/hourglass.pipeline @@ -0,0 +1,32 @@ +{ + "hourglass": { + "stream_config": { + "deviceId": "0" + }, + "appsrc0": { + "props": { + "blocksize": "409600" + }, + "factory": "appsrc", + "next": "mxpi_tensorinfer0" + }, + "mxpi_tensorinfer0": { + "props": { + "dataSource": "appsrc0", + "modelPath": "../convert/hourglass.om", + "waitingTime": "2000", + "outputDeviceId": "-1" + }, + "factory": "mxpi_tensorinfer", + "next": "appsink0" + }, + "appsink0": { + "props": { + "blocksize": "4096000" + }, + "factory": "appsink" + } + } +} + + diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/convert/atc.sh b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/convert/atc.sh new file mode 100644 index 0000000000000000000000000000000000000000..ce9bfd101a0800a934228b5fa32878e156a50340 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/convert/atc.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +onnx_path=$1 +om_path=$2 +echo "Input ONNX file path: ${onnx_path}" +echo "Output OM file path: ${om_path}" + +atc --model="${onnx_path}" \ + --framework=5 \ + --output="${om_path}" \ + --soc_version=Ascend310 \ + --input_format=NCHW \ + --input_shape="input.1:1,3,384,384" \ + --output_type=FP32 \ \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/docker_start_infer.sh b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/docker_start_infer.sh new file mode 100644 index 0000000000000000000000000000000000000000..c76879755acc27849fd8ca75ab0fcd76daea1658 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/docker_start_infer.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +docker_image=$1 +share_dir=$2 +data_dir=$3 +echo "$1" +echo "$2" +if [ -z "${docker_image}" ]; then + echo "please input docker_image" + exit 1 +fi + +if [ ! -d "${share_dir}" ]; then + echo "please input share directory that contains dataset, models and codes" + exit 1 +fi + + +docker run -it -u root \ + --device=/dev/davinci0 \ + --device=/dev/davinci_manager \ + --device=/dev/devmm_svm \ + --device=/dev/hisi_hdc \ + --privileged \ + -v //usr/local/bin/npu-smi:/usr/local/bin/npu-smi \ + -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ + -v ${data_dir}:${data_dir} \ + -v ${share_dir}:${share_dir} \ + ${docker_image} \ + /bin/bash diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/CMakeLists.txt b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..298e2d92fc55c9cb06ea829c4ecc5237e5b02abb --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.10.2) +project(hourglass) +set(TARGET hourglass) +add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) +add_definitions(-Dgoogle=mindxsdk_private) +set(MX_SDK_HOME "$ENV{MX_SDK_HOME}") +file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/src/*cpp ) + +include_directories(${MX_SDK_HOME}/include) +include_directories(${PROJECT_SOURCE_DIR}/include) +include_directories(${MX_SDK_HOME}/opensource/include) +include_directories(${MX_SDK_HOME}/opensource/include/opencv4) +include_directories(${MX_SDK_HOME}/lib) + +link_directories( + ${MX_SDK_HOME}/lib + ${MX_SDK_HOME}/opensource/lib + ${MX_SDK_HOME}/lib/modelpostprocessors + /usr/local/Ascend/driver/lib64/ +) + +add_compile_options(-std=c++11 -fPIC -fstack-protector-all -pie -Wno-deprecated-declarations) +add_executable(${TARGET} ${SRC_FILES}) +target_link_libraries(${TARGET} + glog + mxbase + cpprest + opencv_world + ) +install(TARGETS ${TARGET} RUNTIME DESTINATION ${PROJECT_SOURCE_DIR}/) \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/build.sh b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..2f142c1839924ef1f62912df4e0f56fe0a980590 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/build.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +path_cur="$(dirname "$0")" + +function check_env() +{ + # set ASCEND_VERSION to ascend-toolkit/latest when it was not specified by user + if [ ! "${ASCEND_VERSION}" ]; then + export ASCEND_VERSION=ascend-toolkit/latest + echo "Set ASCEND_VERSION to the default value: ${ASCEND_VERSION}" + else + echo "ASCEND_VERSION is set to ${ASCEND_VERSION} by user" + fi + + if [ ! "${ARCH_PATTERN}" ]; then + # set ARCH_PATTERN to ./ when it was not specified by user + export ARCH_PATTERN=./ + echo "ARCH_PATTERN is set to the default value: ${ARCH_PATTERN}" + else + echo "ARCH_PATTERN is set to ${ARCH_PATTERN} by user" + fi +} + +function build_hourglass() +{ + cd "$path_cur" || exit + rm -rf result + mkdir -p result + rm -rf build + mkdir -p build + cd build || exit + cmake .. + make + ret=$? + if [ ${ret} -ne 0 ]; then + echo "Failed to build hourglass." + exit ${ret} + fi + make install +} + +check_env +build_hourglass \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/include/hourglass.h b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/include/hourglass.h new file mode 100644 index 0000000000000000000000000000000000000000..0b6d53948f82f80c57a4de7da274ebbd1104e3c3 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/include/hourglass.h @@ -0,0 +1,75 @@ +/* + * Copyright 2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HOURGLASS_H +#define HOURGLASS_H + +#include +#include +#include "postprocess.h" +#include +#include "MxBase/Log/Log.h" +#include "MxBase/ModelInfer/ModelInferenceProcessor.h" +#include "MxBase/Tensor/TensorContext/TensorContext.h" +#include "MxBase/DeviceManager/DeviceManager.h" + + +namespace { + const uint32_t MODEL_HEIGHT = 384; + const uint32_t MODEL_WIDTH = 384; + const int NUMS_JOINTS = 16; +} + +struct InitParam { + uint32_t deviceId; + uint32_t res; // resolution of model input + std::string modelPath; + std::string csvPath; +}; + +struct ImageShape { + uint32_t width; + uint32_t height; +}; + +struct ImageAnnot { + std::string imageName; + float center[2]; + float scale[2]; +}; + +class Hourglass { +public: + APP_ERROR Init(const InitParam &initParam); + APP_ERROR DeInit(); + APP_ERROR ReadImage(const std::string &imgPath, cv::Mat &imageMat, ImageShape &imgShape); + APP_ERROR Resize_Affine(const cv::Mat& srcImage, cv::Mat *dstImage, ImageShape *imgShape, + const float center[], const float scale[]); + APP_ERROR CVMatToTensorBase(const cv::Mat& imageMat, MxBase::TensorBase *tensorBase); + APP_ERROR Inference(const std::vector &inputs, + std::vector *outputs); + APP_ERROR PostProcess(const std::vector &inputs, + std::vector >* node_score_list, + const float center[], const float scale[]); + APP_ERROR Process(const ImageAnnot &imageAnnot); +private: + std::shared_ptr model_; + MxBase::ModelDesc modelDesc_; + std::shared_ptr hourglassPostprocess; + uint32_t deviceId_ = 0; + uint32_t res = 384; +}; +#endif diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/include/postprocess.h b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/include/postprocess.h new file mode 100644 index 0000000000000000000000000000000000000000..35d2080e1081d04cb265b7949e7ff26154b5506e --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/include/postprocess.h @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HOURGLASS_POSTPROCESS_H +#define HOURGLASS_POSTPROCESS_H + +#include +#include +#include +#include "MxBase/ErrorCode/ErrorCode.h" +#include "MxBase/PostProcessBases/ObjectPostProcessBase.h" + + +namespace { + auto floatDeleter = [](float* p) {}; + const int SCALE_RATIO = 200; + const int NPOINTS = 16; + const int HEIGHT_HEAPMAP = 96; + const int WIDTH_HEAPMAP = 96; + const int NUMS_HEAPMAP = HEIGHT_HEAPMAP * WIDTH_HEAPMAP; + float heatmaps_reshape[NPOINTS][NUMS_HEAPMAP] = {}; + float batch_heatmaps[NPOINTS][HEIGHT_HEAPMAP][WIDTH_HEAPMAP] = {}; +} + + +class HourglassPostprocess : public MxBase::ObjectPostProcessBase { +public: + APP_ERROR Init(); + APP_ERROR DeInit(); + APP_ERROR Process(const float center[], const float scale[], + const std::vector &tensors, + std::vector >* node_score_list); +private: + void GetHeatmap(const std::vector& tensors, + uint32_t heatmapHeight, uint32_t heatmapWeight); + int GetIntData(const int index, const float(*heatmaps_reshape)[NUMS_HEAPMAP]); + double GetFloatData(const int index, const float(*heatmaps_reshape)[NUMS_HEAPMAP]); + void GetAffineMatrix(const float center[], const float scale[], cv::Mat *warp_mat); + void ParseHeatmap(const std::vector& tensors, + std::vector *preds_result, + uint32_t heatmapHeight, uint32_t heatmapWeight, + const float center[], const float scale[]); +}; + +#endif diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/src/hourglass.cpp b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/src/hourglass.cpp new file mode 100644 index 0000000000000000000000000000000000000000..76989494d47d8ffed726d37d0234e420e9270244 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/src/hourglass.cpp @@ -0,0 +1,322 @@ +/* + * Copyright 2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hourglass.h" + + +APP_ERROR Hourglass::Init(const InitParam &initParam) { + deviceId_ = initParam.deviceId; + res = initParam.res; + + APP_ERROR ret = MxBase::DeviceManager::GetInstance()->InitDevices(); + if (ret != APP_ERR_OK) { + LogError << "Init devices failed, ret=" << ret << "."; + return ret; + } + ret = MxBase::TensorContext::GetInstance()->SetContext(initParam.deviceId); + if (ret != APP_ERR_OK) { + LogError << "Set context failed, ret=" << ret << "."; + return ret; + } + model_ = std::make_shared(); + ret = model_->Init(initParam.modelPath, modelDesc_); + if (ret != APP_ERR_OK) { + LogError << "ModelInferenceProcessor init failed, ret=" << ret << "."; + return ret; + } + hourglassPostprocess = std::make_shared(); + ret = hourglassPostprocess->Init(); + if (ret != APP_ERR_OK) { + LogError << "HourglassPostprocess init failed, ret=" << ret << "."; + return ret; + } + return APP_ERR_OK; +} + + +APP_ERROR Hourglass::DeInit() { + model_->DeInit(); + hourglassPostprocess->DeInit(); + MxBase::DeviceManager::GetInstance()->DestroyDevices(); + return APP_ERR_OK; +} + + +APP_ERROR Hourglass::ReadImage(const std::string &imgPath, cv::Mat &imageMat, ImageShape &imgShape) { + cv::Mat bgrImageMat; + bgrImageMat = cv::imread(imgPath, cv::IMREAD_COLOR); + cv::cvtColor(bgrImageMat, imageMat, cv::COLOR_BGR2RGB); + imgShape.width = imageMat.cols; + imgShape.height = imageMat.rows; + LogInfo << "ImageSize:" << imageMat.size(); + LogInfo << "ImageDims:" << imageMat.dims; + LogInfo << "ImageChannels:" << imageMat.channels(); + return APP_ERR_OK; +} + + +APP_ERROR Hourglass::Resize_Affine(const cv::Mat& srcImage, cv::Mat *dstImage, ImageShape *imgShape, + const float center[], const float scale[]) { + int new_width, new_height; + new_height = static_cast(imgShape->height); + new_width = static_cast(imgShape->width); + + float scale_tem[2] = {}; + scale_tem[0] = scale[0] * 200.0; + scale_tem[1] = scale[1] * 200.0; + float src_w = scale_tem[0]; + float dst_w = MODEL_WIDTH; + float dst_h = MODEL_HEIGHT; + float src_dir[2] = {}; + float dst_dir[2] = {}; + float sn = sin(0); + float cs = cos(0); + src_dir[0] = src_w * 0.5 * sn; + src_dir[1] = src_w * (-0.5) * cs; + dst_dir[0] = 0; + dst_dir[1] = dst_w * (-0.5); + + float src[3][2] = {}; + float dst[3][2] = {}; + + src[0][0] = center[0]; + src[0][1] = center[1]; + src[1][0] = center[0] + src_dir[0]; + src[1][1] = center[1] + src_dir[1]; + dst[0][0] = dst_w * 0.5; + dst[0][1] = dst_h * 0.5; + dst[1][0] = dst_w * 0.5 + dst_dir[0]; + dst[1][1] = dst_h * 0.5 + dst_dir[1]; + + float src_direct[2] = {}; + src_direct[0] = src[0][0] - src[1][0]; + src_direct[1] = src[0][1] - src[1][1]; + src[2][0] = src[1][0] - src_direct[1]; + src[2][1] = src[1][1] + src_direct[0]; + + float dst_direct[2] = {}; + dst_direct[0] = dst[0][0] - dst[1][0]; + dst_direct[1] = dst[0][1] - dst[1][1]; + dst[2][0] = dst[1][0] - dst_direct[1]; + dst[2][1] = dst[1][1] + dst_direct[0]; + cv::Point2f srcPoint2f[3], dstPoint2f[3]; + srcPoint2f[0] = cv::Point2f(static_cast(src[0][0]), static_cast(src[0][1])); + srcPoint2f[1] = cv::Point2f(static_cast(src[1][0]), static_cast(src[1][1])); + srcPoint2f[2] = cv::Point2f(static_cast(src[2][0]), static_cast(src[2][1])); + dstPoint2f[0] = cv::Point2f(static_cast(dst[0][0]), static_cast(dst[0][1])); + dstPoint2f[1] = cv::Point2f(static_cast(dst[1][0]), static_cast(dst[1][1])); + dstPoint2f[2] = cv::Point2f(static_cast(dst[2][0]), static_cast(dst[2][1])); + cv::Mat warp_mat(2, 3, CV_32FC1); + warp_mat = cv::getAffineTransform(srcPoint2f, dstPoint2f); + + cv::Mat src_cv(new_height, new_width, CV_8UC3, srcImage.data); + + cv::Mat warp_dst = cv::Mat::zeros(cv::Size(static_cast(MODEL_WIDTH), + static_cast(MODEL_HEIGHT)), src_cv.type()); + + cv::warpAffine(src_cv, warp_dst, warp_mat, warp_dst.size()); + + cv::Mat image_finally(warp_dst.rows, warp_dst.cols, CV_32FC3); + + warp_dst.convertTo(image_finally, CV_32FC3, 1 / 255.0); + + float mean[3] = { 0.485, 0.456, 0.406 }; + float std[3] = { 0.229, 0.224, 0.225 }; + + for (int i = 0; i < image_finally.rows; i++) { + for (int j = 0; j < image_finally.cols; j++) { + if (warp_dst.channels() == 3) { + image_finally.at(i, j)[0]= (image_finally.at(i, j)[0] - mean[0]) / std[0]; + image_finally.at(i, j)[1]= (image_finally.at(i, j)[1] - mean[1]) / std[1]; + image_finally.at(i, j)[2]= (image_finally.at(i, j)[2] - mean[2]) / std[2]; + } + } + } + *dstImage = image_finally; + return APP_ERR_OK; +} + + + +APP_ERROR Hourglass::CVMatToTensorBase(const cv::Mat& imageMat, MxBase::TensorBase *tensorBase) { + uint32_t dataSize = 1; + for (size_t i = 0; i < modelDesc_.inputTensors.size(); ++i) { + std::vector shape = {}; + for (size_t j = 0; j < modelDesc_.inputTensors[i].tensorDims.size(); ++j) { + shape.push_back((uint32_t)modelDesc_.inputTensors[i].tensorDims[j]); + } + for (uint32_t s = 0; s < shape.size(); ++s) { + dataSize *= shape[s]; + } + } + // mat NHWC to NCHW + size_t H = 384, W = 384, C = 3; + float mat_data[dataSize] = {}; + dataSize = dataSize * 4; + for (size_t c = 0; c < C; c++) { + for (size_t h = 0; h < H; h++) { + for (size_t w = 0; w < W; w++) { + int id = c * (H * W) + h * W + w; + mat_data[id] = imageMat.at(h, w)[c]; + } + } + } + MxBase::MemoryData memoryDataDst(dataSize, MxBase::MemoryData::MEMORY_DEVICE, deviceId_); + MxBase::MemoryData memoryDataSrc(mat_data, dataSize, MxBase::MemoryData::MEMORY_HOST_MALLOC); + APP_ERROR ret = MxBase::MemoryHelper::MxbsMallocAndCopy(memoryDataDst, memoryDataSrc); + if (ret != APP_ERR_OK) { + LogError << GetError(ret) << "Memory malloc failed."; + return ret; + } + std::vector shape = { 1, 3, 384, 384 }; + *tensorBase = MxBase::TensorBase(memoryDataDst, false, shape, MxBase::TENSOR_DTYPE_FLOAT32); + return APP_ERR_OK; +} + + +APP_ERROR Hourglass::Inference(const std::vector &inputs, + std::vector *outputs) { + auto dtypes = model_->GetOutputDataType(); + for (size_t i = 0; i < modelDesc_.outputTensors.size(); ++i) { + std::vector shape = {}; + for (size_t j = 0; j < modelDesc_.outputTensors[i].tensorDims.size(); ++j) { + shape.push_back((uint32_t)modelDesc_.outputTensors[i].tensorDims[j]); + } + MxBase::TensorBase tensor(shape, dtypes[i], MxBase::MemoryData::MemoryType::MEMORY_DEVICE, deviceId_); + APP_ERROR ret = MxBase::TensorBase::TensorBaseMalloc(tensor); + if (ret != APP_ERR_OK) { + LogError << "TensorBaseMalloc failed, ret=" << ret << "."; + return ret; + } + outputs->push_back(tensor); + } + MxBase::DynamicInfo dynamicInfo = {}; + dynamicInfo.dynamicType = MxBase::DynamicType::STATIC_BATCH; + dynamicInfo.batchSize = 1; + + APP_ERROR ret = model_->ModelInference(inputs, *outputs, dynamicInfo); + if (ret != APP_ERR_OK) { + LogError << "ModelInference failed, ret=" << ret << "."; + return ret; + } + return APP_ERR_OK; +} + + +APP_ERROR Hourglass::PostProcess(const std::vector &inputs, + std::vector >* node_score_list, + const float center[], const float scale[]) { + APP_ERROR ret = hourglassPostprocess->Process(center, scale, inputs, node_score_list); + if (ret != APP_ERR_OK) { + LogError << "Hourglass Postprocess failed, ret=" << ret << "."; + return ret; + } + return APP_ERR_OK; +} + + +void drawSkeletonLine(const std::string& imgPath, const std::string& resultPath, const float (*preds)[2]) { + cv::Mat imageMat = cv::imread(imgPath); // read orignal image + + // Draw keypoints + for (int i = 0; i < NUMS_JOINTS; i++) { + cv::Point center; + center.x = static_cast(preds[i][0]); + center.y = static_cast(preds[i][1]); + cv::circle(imageMat, center, 4, cv::Scalar(0, 255, 85), -1); + } + + // draw skeleton lines + std::vector> skeletonLines = {{0, 1}, {1, 2}, {2, 12}, {12, 11}, {11, 10}, {12, 7}, + {7, 8}, {8, 9}, {7, 6}, {7, 13}, {13, 14}, {14, 15}, + {13, 3}, {3, 6}, {6, 2}, {3, 4}, {4, 5}}; + for (int i = 0; i < skeletonLines.size(); i++) { + int x1 = int(preds[skeletonLines[i][0]][0]); + int y1 = int(preds[skeletonLines[i][0]][1]); + int x2 = int(preds[skeletonLines[i][1]][0]); + int y2 = int(preds[skeletonLines[i][1]][1]); + cv::line(imageMat, cv::Point(x1, y1), cv::Point(x2, y2), (255, 0, 0), 3); + } + + cv::imwrite(resultPath, imageMat); // save image +} + + +void VisualizeInferResult(const std::string& imgPath, const std::string &resultPath, + const std::vector >& node_score_list) { + for (int i = 0; i < node_score_list.size(); i++) { + float preds[NUMS_JOINTS][2] = {}; + float maxvals[NUMS_JOINTS] = {}; + int idx = 0; + for (int j = 0; j < node_score_list[i].size(); j += 3) { + preds[idx][0] = node_score_list[i][j]; + preds[idx][1] = node_score_list[i][j + 1]; + maxvals[idx] = node_score_list[i][j + 2]; + idx++; + } + LogInfo << "infer result:"; + LogInfo << "preds:"; + for (int m = 0; m < NUMS_JOINTS; m++) { + LogInfo << preds[m][0] << " " << preds[m][1]; + } + drawSkeletonLine(imgPath, resultPath, preds); + } +} + + +APP_ERROR Hourglass::Process(const ImageAnnot &imageAnnot) { + cv::Mat imageMat; + ImageShape imageShape{}; + std::string imagePath = "../../data/mpii/images/"; // 数据集图片目录 + APP_ERROR ret = ReadImage(imagePath + imageAnnot.imageName, imageMat, imageShape); + if (ret != APP_ERR_OK) { + LogError << "ReadImage failed, ret=" << ret << "."; + return ret; + } + + float center[2] = {imageAnnot.center[0], imageAnnot.center[1]}; + float scale[2] = {imageAnnot.scale[0], imageAnnot.scale[1]}; + cv::Mat dstImage; + Resize_Affine(imageMat, &dstImage, &imageShape, center, scale); + + std::vector inputs = {}; + std::vector outputs = {}; + MxBase::TensorBase tensorBase; + ret = CVMatToTensorBase(dstImage, &tensorBase); + if (ret != APP_ERR_OK) { + LogError << "CVMatToTensorBase failed, ret=" << ret << "."; + return ret; + } + inputs.push_back(tensorBase); + + ret = Inference(inputs, &outputs); + if (ret != APP_ERR_OK) { + LogError << "Inference failed, ret=" << ret << "."; + return ret; + } + LogInfo << "Inference success, ret=" << ret << "."; + + std::vector > node_score_list = {}; + ret = PostProcess(outputs, &node_score_list, center, scale); + if (ret != APP_ERR_OK) { + LogError << "PostProcess failed, ret=" << ret << "."; + return ret; + } + + VisualizeInferResult(imagePath + imageAnnot.imageName, "./result/" + imageAnnot.imageName, node_score_list); + + return APP_ERR_OK; +} diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/src/main.cpp b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc05bc1dd1325b4ee79ef0e61b9ca47f3a516fb2 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/src/main.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hourglass.h" +#include "MxBase/Log/Log.h" +#include +#include +#include +#include +#include +#include + +using namespace std; + + +void InitHourglassParam(InitParam &initParam) { + initParam.deviceId = 0; + initParam.res = 384; // resolution of model input + initParam.modelPath = "../convert/hourglass.om"; + initParam.csvPath = "../data/valid.csv"; +} + +void ReadAnnot(int idx, ImageAnnot &imageAnnot, const InitParam &initParam) { + ifstream inFile(initParam.csvPath, std::ios::in); + if (!inFile) { + LogError << "OPEN csvFile ERROR, csvPath is " << initParam.csvPath << "."; + exit(1); + } + + string line; + string field; + int lineNum = idx; + int num = 0; + while (getline(inFile, line)) { + num++; + if(num == lineNum) { + istringstream sin(line); + getline(sin, field, ','); + imageAnnot.imageName = field; + getline(sin, field, ','); + imageAnnot.center[0] = strtod(field.c_str(), NULL); + getline(sin, field, ','); + imageAnnot.center[1] = strtod(field.c_str(), NULL); + getline(sin, field, ','); + imageAnnot.scale[0] = strtod(field.c_str(), NULL); + getline(sin, field); + imageAnnot.scale[1] = strtod(field.c_str(), NULL); + break; + } + + } + inFile.close(); +} + + +int main(int argc, char* argv[]) { + if (argc <= 1) { + LogWarn << "Please input an integer image index whitch is between 1 and 2985."; + return APP_ERR_OK; + } + + int num = atoi(argv[1]); // get number of valid dataset whitch is between 1 and 2985. + if (num <= 0 || num > 2958) { + LogError << "image number is invalid, number must be an integer number between 1 and 2985."; + return APP_ERR_OK; + } + + InitParam initParam; + InitHourglassParam(initParam); + ImageAnnot imageAnnot; + + LogInfo << "model path: " << initParam.modelPath; + LogInfo << "csvfile path: " << initParam.csvPath; + + auto hourglass = std::make_shared(); + APP_ERROR ret = hourglass->Init(initParam); + if (ret != APP_ERR_OK) { + LogError << "Hourglass init failed, ret=" << ret << "."; + return ret; + } + + for (int i = 1; i <= num; i++) { + ReadAnnot(i, imageAnnot, initParam); + LogInfo << "image name: " << imageAnnot.imageName; + LogInfo << "resize_h: " << initParam.res; + LogInfo << "resize_w: " << initParam.res; + ret = hourglass->Process(imageAnnot); + if (ret != APP_ERR_OK) { + LogError << "Hourglass process failed, ret=" << ret << "."; + hourglass->DeInit(); + return ret; + } + } + + hourglass->DeInit(); + + return APP_ERR_OK; +} \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/src/postprocess.cpp b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/src/postprocess.cpp new file mode 100644 index 0000000000000000000000000000000000000000..644140f0ac9ae4eef687871d53854b931ff63d5c --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/mxbase/src/postprocess.cpp @@ -0,0 +1,217 @@ +/* + * Copyright 2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "postprocess.h" + + +APP_ERROR HourglassPostprocess::Init() { + LogInfo << "Begin to initialize HourglassPostprocess."; + LogInfo << "End to initialize HourglassPostprocess."; + return APP_ERR_OK; +} + + +APP_ERROR HourglassPostprocess::DeInit() { + LogInfo << "Begin to deinitialize HourglassPostprocess."; + LogInfo << "End to deinitialize HourglassPostprocess."; + return APP_ERR_OK; +} + + +void HourglassPostprocess::GetHeatmap(const std::vector& tensors, + uint32_t heatmapHeight, uint32_t heatmapWeight) { + auto bboxPtr = reinterpret_cast(tensors[0].GetBuffer()); + std::shared_ptr keypoint_pointer; + keypoint_pointer.reset(bboxPtr, floatDeleter); + + for (size_t i = 0; i < NPOINTS; i++) { + int startIndex = i * heatmapHeight * heatmapWeight; + for (size_t j = 0; j < heatmapHeight; j++) { + int middleIndex = j * heatmapWeight; + for (size_t k = 0; k < heatmapWeight; k++) { + float x = static_cast(keypoint_pointer.get())[startIndex + j * heatmapWeight + k]; + heatmaps_reshape[i][j * heatmapWeight + k] = x; + batch_heatmaps[i][j][k] = x; + } + } + } +} + + +int HourglassPostprocess::GetIntData(const int index, const float(*heatmaps_reshape)[NUMS_HEAPMAP]) { + int idx_tem = 0; + float tem = 0; + for (int j = 0; j < NUMS_HEAPMAP; j++) { + if (heatmaps_reshape[index][j] > tem) { + tem = heatmaps_reshape[index][j]; + idx_tem = j; + } + } + return idx_tem; +} + + +double HourglassPostprocess::GetFloatData(const int index, const float(*heatmaps_reshape)[NUMS_HEAPMAP]) { + float tem = 0; + for (int j = 0; j < NUMS_HEAPMAP; j++) { + if (heatmaps_reshape[index][j] > tem) { + tem = heatmaps_reshape[index][j]; + } + } + return tem; +} + + +void HourglassPostprocess::GetAffineMatrix(const float center[], const float scale[], cv::Mat *warp_mat) { + float scale_tem[2] = {}; + scale_tem[0] = scale[0] * SCALE_RATIO; + scale_tem[1] = scale[1] * SCALE_RATIO; + float src_w = scale_tem[0]; + float dst_w = WIDTH_HEAPMAP; + float dst_h = HEIGHT_HEAPMAP; + float src_dir[2] = {}; + float dst_dir[2] = {}; + + float sn = sin(0); + float cs = cos(0); + src_dir[0] = src_w * 0.5 * sn; + src_dir[1] = src_w * (-0.5) * cs; + dst_dir[0] = 0; + dst_dir[1] = dst_w * (-0.5); + + float src[3][2] = {}; + float dst[3][2] = {}; + + src[0][0] = center[0]; + src[0][1] = center[1]; + src[1][0] = center[0] + src_dir[0]; + src[1][1] = center[1] + src_dir[1]; + dst[0][0] = dst_w * 0.5; + dst[0][1] = dst_h * 0.5; + dst[1][0] = dst_w * 0.5 + dst_dir[0]; + dst[1][1] = dst_h * 0.5 + dst_dir[1]; + + float src_direct[2] = {}; + src_direct[0] = src[0][0] - src[1][0]; + src_direct[1] = src[0][1] - src[1][1]; + src[2][0] = src[1][0] - src_direct[1]; + src[2][1] = src[1][1] + src_direct[0]; + + float dst_direct[2] = {}; + dst_direct[0] = dst[0][0] - dst[1][0]; + dst_direct[1] = dst[0][1] - dst[1][1]; + dst[2][0] = dst[1][0] - dst_direct[1]; + dst[2][1] = dst[1][1] + dst_direct[0]; + cv::Point2f srcPoint2f[3], dstPoint2f[3]; + srcPoint2f[0] = cv::Point2f(static_cast(src[0][0]), static_cast(src[0][1])); + srcPoint2f[1] = cv::Point2f(static_cast(src[1][0]), static_cast(src[1][1])); + srcPoint2f[2] = cv::Point2f(static_cast(src[2][0]), static_cast(src[2][1])); + dstPoint2f[0] = cv::Point2f(static_cast(dst[0][0]), static_cast(dst[0][1])); + dstPoint2f[1] = cv::Point2f(static_cast(dst[1][0]), static_cast(dst[1][1])); + dstPoint2f[2] = cv::Point2f(static_cast(dst[2][0]), static_cast(dst[2][1])); + cv::Mat warp_mat_af(2, 3, CV_32FC1); + warp_mat_af = cv::getAffineTransform(dstPoint2f, srcPoint2f); + *warp_mat = warp_mat_af; +} + + +void HourglassPostprocess::ParseHeatmap(const std::vector& tensors, + std::vector *preds_result, + uint32_t heatmapHeight, uint32_t heatmapWeight, + const float center[], const float scale[]) { + LogInfo << "Begin to ParseHeatmap."; + GetHeatmap(tensors, heatmapHeight, heatmapWeight); + float maxvals[NPOINTS] = {}; + int idx[NPOINTS] = {}; + for (size_t i = 0; i < NPOINTS; i++) { + maxvals[i] = GetFloatData(i, heatmaps_reshape); + idx[i] = GetIntData(i, heatmaps_reshape); + } + float preds[NPOINTS][2] = {}; + for (size_t i = 0; i < NPOINTS; i++) { + preds[i][0] = (idx[i]) % heatmapWeight; + preds[i][1] = floor(idx[i] / heatmapWeight); + if (maxvals[i] < 0) { + preds[i][0] = preds[i][0] * (-1); + preds[i][1] = preds[i][0] * (-1); + } + } + for (size_t i = 0; i < NPOINTS; i++) { + float hm[HEIGHT_HEAPMAP][WIDTH_HEAPMAP] = {}; + for (size_t m = 0; m < HEIGHT_HEAPMAP; m++) { + for (size_t n = 0; n < WIDTH_HEAPMAP; n++) { + hm[m][n] = batch_heatmaps[i][m][n]; + } + } + int px = static_cast(floor(preds[i][0] + 0.5)); + int py = static_cast(floor(preds[i][1] + 0.5)); + if (px > 1 && px < heatmapWeight - 1 && py>1 && py < heatmapHeight - 1) { + float diff_x = hm[py][px + 1] - hm[py][px - 1]; + float diff_y = hm[py + 1][px] - hm[py - 1][px]; + if (diff_x > 0) { + preds[i][0] = preds[i][0] + 0.25; + } + if (diff_x < 0) { + preds[i][0] = preds[i][0] - 0.25; + } + if (diff_y > 0) { + preds[i][1] = preds[i][1] + 0.25; + } + if (diff_y < 0) { + preds[i][1] = preds[i][1] - 0.25; + } + } + } + cv::Mat warp_mat(2, 3, CV_32FC1); + GetAffineMatrix(center, scale, &warp_mat); + for (size_t i = 0; i < NPOINTS; i++) { + preds[i][0] = preds[i][0] * warp_mat.at(0, 0) + + preds[i][1] * warp_mat.at(0, 1) + warp_mat.at(0, 2); + preds[i][1] = preds[i][0] * warp_mat.at(1, 0) + + preds[i][1] * warp_mat.at(1, 1) + warp_mat.at(1, 2); + } + for (size_t i = 0; i < NPOINTS; i++) { + preds_result->push_back(preds[i][0]); + preds_result->push_back(preds[i][1]); + preds_result->push_back(maxvals[i]); + } +} + + +APP_ERROR HourglassPostprocess::Process(const float center[], const float scale[], + const std::vector &tensors, + std::vector>* node_score_list) { + LogDebug << "Begin to Hourglass PostProcess."; + auto inputs = tensors; + APP_ERROR ret = CheckAndMoveTensors(inputs); + if (ret != APP_ERR_OK) { + LogError << "CheckAndMoveTensors failed, ret=" << ret; + return ret; + } + + auto shape = inputs[0].GetShape(); + uint32_t batchSize = shape[0]; + uint32_t heatmapHeight = shape[2]; + uint32_t heatmapWeight = shape[3]; + for (uint32_t i = 0; i < batchSize; ++i) { + std::vector preds_result; + ParseHeatmap(inputs, &preds_result, heatmapHeight, heatmapWeight, center, scale); + node_score_list->push_back(preds_result); + } + LogInfo << "End to Hourglass PostProcess."; + return APP_ERR_OK; +} + diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/cal_accuracy.py b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/cal_accuracy.py new file mode 100644 index 0000000000000000000000000000000000000000..c6cb42d80a3860e4339ae80651699c349f97e8f6 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/cal_accuracy.py @@ -0,0 +1,133 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +import copy +import numpy as np + + +class MPIIEval: + """ + eval for MPII dataset with PCK + """ + template = { + "all": { + "total": 0, + "ankle": 0, + "knee": 0, + "hip": 0, + "pelvis": 0, + "thorax": 0, + "neck": 0, + "head": 0, + "wrist": 0, + "elbow": 0, + "shoulder": 0, + }, + "visible": { + "total": 0, + "ankle": 0, + "knee": 0, + "hip": 0, + "pelvis": 0, + "thorax": 0, + "neck": 0, + "head": 0, + "wrist": 0, + "elbow": 0, + "shoulder": 0, + }, + "not visible": { + "total": 0, + "ankle": 0, + "knee": 0, + "hip": 0, + "pelvis": 0, + "thorax": 0, + "neck": 0, + "head": 0, + "wrist": 0, + "elbow": 0, + "shoulder": 0, + }, + } + + joint_map = [ + "ankle", + "knee", + "hip", + "hip", + "knee", + "ankle", + "pelvis", + "thorax", + "neck", + "head", + "wrist", + "elbow", + "shoulder", + "shoulder", + "elbow", + "wrist", + ] + + def __init__(self): + self.correct_valid = copy.deepcopy(self.template) + self.count_valid = copy.deepcopy(self.template) + + def eval(self, pred, gt, normalizing, bound=0.5): + """ + use PCK with threshold of .5 of normalized distance (presumably head size) + """ + for p, g, normalize in zip(pred, gt, normalizing): + for j in range(g.shape[1]): + vis = "visible" + if g[0, j, 0] == 0: # Not in image + continue + if g[0, j, 2] == 0: + vis = "not visible" + joint = self.joint_map[j] + + self.count_valid["all"]["total"] += 1 + self.count_valid["all"][joint] += 1 + self.count_valid[vis]["total"] += 1 + self.count_valid[vis][joint] += 1 + + error = np.linalg.norm(p[0]["keypoints"][j, :2] - g[0, j, :2]) / normalize + + if bound > error: + self.correct_valid["all"]["total"] += 1 + self.correct_valid["all"][joint] += 1 + self.correct_valid[vis]["total"] += 1 + self.correct_valid[vis][joint] += 1 + self.output_result(bound) + + def output_result(self, bound): + """ + output split via valid + """ + for k in self.correct_valid: + print(k, ":") + for key in self.correct_valid[k]: + print( + "Val PCK @,", + bound, + ",", + key, + ":", + round(self.correct_valid[k][key] / max(self.count_valid[k][key], 1), 3), + ", count:", + self.count_valid[k][key], + ) + print("\n") \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/main.py b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/main.py new file mode 100644 index 0000000000000000000000000000000000000000..940bf187e509c8a5dcbce0aa29e4644dfb254894 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/main.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# coding=utf-8 + +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import cv2 +import argparse +import numpy as np +from preprocess import get_img +from postprocess import get_final_preds +from cal_accuracy import MPIIEval +import MxpiDataType_pb2 as MxpiDataType +from StreamManagerApi import StreamManagerApi, StringVector, InProtobufVector, MxProtobufIn + + +def parser_args(): + parser = argparse.ArgumentParser(description = 'Params of Hourglass Model:') + parser.add_argument("--annot_dir", type=str, default="../../data/mpii/annotations/valid.h5") + parser.add_argument("--img_dir", type=str, default="../../data/mpii/images") + + args = parser.parse_args() + return args + + +def send_source_data(appsrc_id, tensor, stream_name, stream_manager): + """ + Construct the input of the stream, + send inputs data to a specified stream based on streamName. + + Returns: + bool: send data success or not + """ + tensor_package_list = MxpiDataType.MxpiTensorPackageList() + tensor_package = tensor_package_list.tensorPackageVec.add() + array_bytes = tensor.tobytes() + tensor_vec = tensor_package.tensorVec.add() + tensor_vec.deviceId = 0 + tensor_vec.memType = 0 + for k in tensor.shape: + tensor_vec.tensorShape.append(k) + tensor_vec.dataStr = array_bytes + tensor_vec.tensorDataSize = len(array_bytes) + key = "appsrc{}".format(appsrc_id).encode('utf-8') + protobuf_vec = InProtobufVector() + protobuf = MxProtobufIn() + protobuf.key = key + protobuf.type = b'MxTools.MxpiTensorPackageList' + protobuf.protobuf = tensor_package_list.SerializeToString() + protobuf_vec.push_back(protobuf) + + rete = stream_manager.SendProtobuf(stream_name, appsrc_id, protobuf_vec) + if rete < 0: + print("Failed to send data to stream.") + return False + return True + + +def drawSkeletonLine(imgName, locs): + ''' + draw skeleton line on orignal image + ''' + imgPath = "%s/%s" % (args.img_dir, imgName) + orgImg = cv2.imread(imgPath) + + for i in range(locs.shape[1]): + cv2.circle(orgImg, (int(locs[0][i][0]), int(locs[0][i][1])), 3, [0, 255, 85], -1) + + skeleton_line = [[0, 1], [1, 2], [2, 12], [12, 11], [11, 10], [12, 7], [7, 8], [8, 9], + [7, 6], [7, 13], [13, 14], [14, 15], [13, 3], [3, 6], [6, 2], [3, 4], [4, 5]] + for line_points in skeleton_line: + x1 = int(locs[0][line_points[0]][0]) + y1 = int(locs[0][line_points[0]][1]) + x2 = int(locs[0][line_points[1]][0]) + y2 = int(locs[0][line_points[1]][1]) + cv2.line(orgImg, (x1, y1), (x2, y2), (255, 0, 0), 3) + + if not os.path.exists("./infer_result"): + os.mkdir("./infer_result") + savePath = "%s/%s" % ("./infer_result", imgName) + cv2.imwrite(savePath, orgImg) + + +if __name__ == '__main__': + args = parser_args() + + # create stream with pipeline file + streamManagerApi = StreamManagerApi() + ret = streamManagerApi.InitManager() + if ret != 0: + print("Failed to init Stream manager, ret=%s" % str(ret)) + exit() + with open(os.path.realpath("../config/hourglass.pipeline"), 'rb') as f: + pipelineStr = f.read() + ret = streamManagerApi.CreateMultipleStreams(pipelineStr) + if ret != 0: + print("Failed to create Stream, ret=%s" % str(ret)) + exit() + + streamName = b'hourglass' + keys = [b"mxpi_tensorinfer0"] + keyVec = StringVector() + for key in keys: + keyVec.push_back(key) + + gts = [] # ground truth labels + preds = [] # predictions + normalizing = [] # normalizations used for evaluation + + for kps, img, c, s, n, imgName in get_img(args): # pre-process + + # send an image to stream + if not send_source_data(0, img, streamName, streamManagerApi): + exit() + + # get infer result from stream + infer_result = streamManagerApi.GetProtobuf(streamName, 0, keyVec) + if infer_result.size() == 0: + print("infer_result is null") + exit() + if infer_result[0].errorCode != 0: + print("GetProtobuf error. errorCode=%d, errorMsg=%s" % (infer_result[0].errorCode, infer_result[0].data.decode())) + exit() + result = MxpiDataType.MxpiTensorPackageList() + result.ParseFromString(infer_result[0].messageBuf) + result = np.frombuffer(result.tensorPackageVec[0].tensorVec[0].dataStr, dtype=np.float32).reshape([1,16,96,96]) + + # post-process + locs, maxvals = get_final_preds(result.copy(), c, s) + + # visualization + drawSkeletonLine(imgName, locs) + + gts.append(kps) + normalizing.append(n) + pred = [] + for i in range(locs.shape[0]): + pred.append({"keypoints": locs[i, :, :]}) + preds.append(pred) + + # calculate PCK accuracy + bound = 0.5 # PCK's threshold of normalized distance + mpii_eval = MPIIEval() + mpii_eval.eval(preds, gts, normalizing, bound) + + # destroy stream + streamManagerApi.DestroyAllStreams() \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/postprocess.py b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/postprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..b3e4fdc386d46b9172b20e66516036278218cc28 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/postprocess.py @@ -0,0 +1,83 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +import numpy as np +from preprocess import get_affine_transform, affine_transform + + +def transform_preds(coords, center, scale, output_size): + target_coords = np.zeros(coords.shape) + trans = get_affine_transform(center, scale, 0, output_size, inv=1) + for p in range(coords.shape[0]): + target_coords[p, 0:2] = affine_transform(coords[p, 0:2], trans) + return target_coords + + +def get_max_preds(batch_heatmaps): + ''' + get predictions from score maps + heatmaps: numpy.ndarray([batch_size, num_joints, height, width]) + + assert isinstance(batch_heatmaps, np.ndarray), \ + 'batch_heatmaps should be numpy.ndarray' + assert batch_heatmaps.ndim == 4, 'batch_images should be 4-ndim''' + + batch_size = batch_heatmaps.shape[0] + num_joints = batch_heatmaps.shape[1] + width = batch_heatmaps.shape[3] + heatmaps_reshaped = batch_heatmaps.reshape((batch_size, num_joints, -1)) + idx = np.argmax(heatmaps_reshaped, 2) + maxvals = np.amax(heatmaps_reshaped, 2) + + maxvals = maxvals.reshape((batch_size, num_joints, 1)) + idx = idx.reshape((batch_size, num_joints, 1)) + + preds = np.tile(idx, (1, 1, 2)).astype(np.float32) + + preds[:, :, 0] = (preds[:, :, 0]) % width + preds[:, :, 1] = np.floor((preds[:, :, 1]) / width) + + pred_mask = np.tile(np.greater(maxvals, 0.0), (1, 1, 2)) + pred_mask = pred_mask.astype(np.float32) + + preds *= pred_mask + return preds, maxvals + + +def get_final_preds(batch_heatmaps, center, scale): + coords, maxvals = get_max_preds(batch_heatmaps) + + heatmap_height = batch_heatmaps.shape[2] + heatmap_width = batch_heatmaps.shape[3] + + # post-processing + for n in range(coords.shape[0]): + for p in range(coords.shape[1]): + hm = batch_heatmaps[n][p] + px = int(math.floor(coords[n][p][0] + 0.5)) + py = int(math.floor(coords[n][p][1] + 0.5)) + if 1 < px < heatmap_width - 1 and 1 < py < heatmap_height - 1: + diff = np.array([hm[py][px + 1] - hm[py][px - 1], + hm[py + 1][px] - hm[py - 1][px]]) + coords[n][p] += np.sign(diff) * .25 + + preds = coords.copy() + + # Transform back + for i in range(coords.shape[0]): + preds[i] = transform_preds(coords[i], center, scale, + [heatmap_width, heatmap_height]) + + return preds, maxvals \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/preprocess.py b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/preprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..503c938c76773db1df7262e5c33700b112a1e36c --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/preprocess.py @@ -0,0 +1,168 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import cv2 +import numpy as np +import h5py + + +def rotate_point(pt, angle_rad): + """Rotate a point by an angle. + + Args: + pt (list[float]): 2 dimensional point to be rotated + angle_rad (float): rotation angle by radian + + Returns: + list[float]: Rotated point. + """ + sn, cs = np.sin(angle_rad), np.cos(angle_rad) + new_x = pt[0] * cs - pt[1] * sn + new_y = pt[0] * sn + pt[1] * cs + rotated_pt = [new_x, new_y] + + return rotated_pt + + +def affine_transform(pt, trans_mat): + """Apply an affine transformation to the points. + + Args: + pt (np.ndarray): a 2 dimensional point to be transformed + trans_mat (np.ndarray): 2x3 matrix of an affine transform + + Returns: + np.ndarray: Transformed points. + """ + new_pt = np.array([pt[0], pt[1], 1.]).T + new_pt = np.dot(trans_mat, new_pt) + + return new_pt[:2] + + +def _get_3rd_point(a, b): + """To calculate the affine matrix, three pairs of points are required. This + function is used to get the 3rd point, given 2D points a & b. + + The 3rd point is defined by rotating vector `a - b` by 90 degrees + anticlockwise, using b as the rotation center. + + Args: + a (np.ndarray): point(x,y) + b (np.ndarray): point(x,y) + + Returns: + np.ndarray: The 3rd point. + """ + direction = a - b + third_pt = b + np.array([-direction[1], direction[0]], dtype=np.float32) + + return third_pt + + +def get_affine_transform(center, + scale, + rot, + output_size, + shift=(0., 0.), + inv=False, + scale_ratio=200.0): + """Get the affine transform matrix, given the center/scale/rot/output_size. + + Args: + center (np.ndarray[2, ]): Center of the bounding box (x, y). + scale (np.ndarray[2, ]): Scale of the bounding box + wrt [width, height]. + rot (float): Rotation angle (degree). + output_size (np.ndarray[2, ]): Size of the destination heatmaps. + shift (0-100%): Shift translation ratio wrt the width/height. + Default (0., 0.). + inv (bool): Option to inverse the affine transform direction. + (inv=False: src->dst or inv=True: dst->src) + scale_ratio(float): pixel std of MPII is 200 + + Returns: + np.ndarray: The transform matrix. + """ + scale_tmp = scale * scale_ratio + + shift = np.array(shift) + src_w = scale_tmp + dst_w = output_size[0] + dst_h = output_size[1] + + rot_rad = np.pi * rot / 180 + src_dir = rotate_point([0., src_w * -0.5], rot_rad) + dst_dir = np.array([0., dst_w * -0.5]) + + src = np.zeros((3, 2), dtype=np.float32) + src[0, :] = center + scale_tmp * shift + src[1, :] = center + src_dir + scale_tmp * shift + src[2, :] = _get_3rd_point(src[0, :], src[1, :]) + + dst = np.zeros((3, 2), dtype=np.float32) + dst[0, :] = [dst_w * 0.5, dst_h * 0.5] + dst[1, :] = np.array([dst_w * 0.5, dst_h * 0.5]) + dst_dir + dst[2, :] = _get_3rd_point(dst[0, :], dst[1, :]) + + if inv: + trans = cv2.getAffineTransform(np.float32(dst), np.float32(src)) + else: + trans = cv2.getAffineTransform(np.float32(src), np.float32(dst)) + + return trans + + +def get_img(args): + """ + load image set + """ + h5file = h5py.File(args.annot_dir, "r") # read lable file + img_nums = 2958 # there are 2958 images in valid.h5 + for i in range(img_nums): + imgName = h5file["imgname"][i].decode("UTF-8") + imgPath = "%s/%s" % (args.img_dir, imgName) + imgBGR = cv2.imread(imgPath, cv2.IMREAD_COLOR) + imgRGB = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2RGB) # transform image format from BGR to RGB + + image_size = (384, 384) # input shape of Hourglass model + + c = h5file["center"][i] + s = h5file["scale"][i] + r = 0 # rotation + + trans = get_affine_transform(c, s, r, image_size) # get affine transform matrix + imgTrans = cv2.warpAffine( + imgRGB, + trans, (int(image_size[0]), int(image_size[1])), + flags = cv2.INTER_LINEAR) + + mean = np.array([0.485, 0.456, 0.406], dtype=np.float32) + std = np.array([0.229, 0.224, 0.225], dtype=np.float32) + imgMeanStd = (imgTrans.astype(np.float32) / 255 - mean) / std + + imgEval = imgMeanStd.reshape((1,) + imgMeanStd.shape) + imgEval = imgEval.transpose(0, 3, 1, 2) # turn image format into NCHW(1*3*384*384) + + kp = h5file["part"][i] + vis = h5file["visible"][i] # if visible vis equals to 1, else vis equals to 0 + kp2 = np.insert(kp, 2, vis, axis=1) + kps = np.zeros((1, 16, 3)) + kps[0] = kp2 + + n = h5file["normalize"][i] + + yield kps, imgEval, c, s, n, imgName + + \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/run.sh b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..345f11e62f283aabcc6a0020860a2dd5434254ac --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/infer/sdk/run.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +help_input1=$1 +annot_dir=$2 +help_input2=$3 +img_dir=$4 + +set -e + +# Simple log helper functions +info() { echo -e "\033[1;34m[INFO ][MxStream] $1\033[1;37m" ; } +warn() { echo >&2 -e "\033[1;31m[WARN ][MxStream] $1\033[1;37m" ; } + +export LD_LIBRARY_PATH=${MX_SDK_HOME}/lib:${MX_SDK_HOME}/opensource/lib:${MX_SDK_HOME}/opensource/lib64:/usr/local/Ascend/ascend-toolkit/latest/acllib/lib64:${LD_LIBRARY_PATH} +export GST_PLUGIN_SCANNER=${MX_SDK_HOME}/opensource/libexec/gstreamer-1.0/gst-plugin-scanner +export GST_PLUGIN_PATH=${MX_SDK_HOME}/opensource/lib/gstreamer-1.0:${MX_SDK_HOME}/lib/plugins + +#to set PYTHONPATH, import the StreamManagerApi.py +export PYTHONPATH=$PYTHONPATH:${MX_SDK_HOME}/python + +python3 main.py $help_input1 $annot_dir $help_input2 $img_dir + +exit 0 \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/selfexport.py b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/selfexport.py new file mode 100644 index 0000000000000000000000000000000000000000..e3b9465b77e0a6885a0fb2d1a9f7955fb3623be6 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/selfexport.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +import mmcv +import torch +from mmcv.runner import load_checkpoint +from mmpose.models import build_posenet +from mmcv.onnx.symbolic import register_extra_symbolics + + +def _convert_batchnorm(module): + """Convert the syncBNs into normal BN3ds.""" + module_output = module + if isinstance(module, torch.nn.SyncBatchNorm): + module_output = torch.nn.BatchNorm3d(module.num_features, module.eps, + module.momentum, module.affine, + module.track_running_stats) + if module.affine: + module_output.weight.data = module.weight.data.clone().detach() + module_output.bias.data = module.bias.data.clone().detach() + module_output.weight.requires_grad = module.weight.requires_grad + module_output.bias.requires_grad = module.bias.requires_grad + module_output.running_mean = module.running_mean + module_output.running_var = module.running_var + module_output.num_batches_tracked = module.num_batches_tracked + for name, child in module.named_children(): + module_output.add_module(name, _convert_batchnorm(child)) + del module + return module_output + + +def build_hourglass(cfg_file, pth_file): + cfg = mmcv.Config.fromfile(cfg_file) + + model = build_posenet(cfg.model) + model = _convert_batchnorm(model) + + if hasattr(model, 'forward_dummy'): + model.forward = model.forward_dummy + else: + raise NotImplementedError( + 'Please implement the forward method for exporting.') + + load_checkpoint(model, pth_file, map_location='cpu') + return model + + +def pth2onnx(cfg_file, + pth_file, + input_shape=[32, 3, 384, 384], + output_file='hourglass.onnx'): + + model = build_hourglass(cfg_file, pth_file) + model.cpu().eval() + + one_img = torch.randn(input_shape).float() + + register_extra_symbolics(11) # MMPose only supports opset 11 + print("===============start to export onnx===============") + torch.onnx.export( + model, + one_img, + output_file, + export_params=True, + keep_initializers_as_inputs=True, + verbose=False, + opset_version=11) + print(f'Successfully exported ONNX model: {output_file}') + diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/train.py b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/train.py new file mode 100644 index 0000000000000000000000000000000000000000..ed07c900a164958b5756127720a403316ec87f95 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/train.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +import argparse +import copy +import os +import os.path as osp +import time + +import mmcv +import torch +from mmcv import Config, DictAction +from mmcv.runner import init_dist, set_random_seed +from mmcv.utils import get_git_hash + +from mmpose import __version__ +from mmpose.apis import train_model +from mmpose.datasets import build_dataset +from mmpose.models import build_posenet +from mmpose.utils import collect_env, get_root_logger + + +def parse_args(): + parser = argparse.ArgumentParser(description='Train a pose model') + + parser.add_argument('--ckpt_interval', type=int, default=1) + parser.add_argument('--eval_interval', type=int, default=5) + parser.add_argument('--lr', type=float, default=5e-5) + parser.add_argument('--warmup_iters', type=int, default=500) + parser.add_argument('--warmup_ratio', type=float, default=0.01) + parser.add_argument('--total_epochs', type=int, default=1) + parser.add_argument('--work_dir', type=str, default="./output") # output file to save log and models + parser.add_argument('--data_root', type=str, default="../data/mpii") # dataset root file + parser.add_argument('--config', type=str, + default="../mmpose-master/configs/top_down/hourglass/mpii/hourglass52_mpii_384x384.py") + + parser.add_argument('--options', nargs='+', action=DictAction, help='arguments in dict') + parser.add_argument('--resume-from', help='the checkpoint file to resume from') + parser.add_argument('--no-validate', action='store_true') + + group_npus = parser.add_mutually_exclusive_group() + group_npus.add_argument('--npus', type=int, default=1, help='number of gpus to use') + group_npus.add_argument('--npu_ids', type=int, nargs='+', help='ids of gpus to use') + + parser.add_argument('--autoscale-lr', + action='store_true', + help='automatically scale lr with the number of gpus') + parser.add_argument('--launcher', + choices=['none', 'pytorch', 'slurm', 'mpi'], + default='pytorch', help='job launcher') + + parser.add_argument('--seed', type=int, default=0, help='random seed') + parser.add_argument('--deterministic', + action='store_true', + help='whether to set deterministic options for CUDNN backend.') + parser.add_argument('--local_rank', type=int, default=0) + + args = parser.parse_args() + if 'LOCAL_RANK' not in os.environ: + os.environ['LOCAL_RANK'] = str(args.local_rank) + + return args + + +def main(): + args = parse_args() + + cfg = Config.fromfile(args.config) + if args.options is not None: + cfg.merge_from_dict(args.options) + + cfg.checkpoint_config.interval = args.ckpt_interval + cfg.evaluation.interval = args.eval_interval + cfg.optimizer.lr = args.lr + cfg.lr_config.warmup_iters = args.warmup_iters + cfg.lr_config.warmup_ratio = args.warmup_ratio + cfg.total_epochs = args.total_epochs + cfg.work_dir = args.work_dir + cfg.data.train.ann_file = f'{args.data_root}/annotations/mpii_train.json' + cfg.data.train.img_prefix = f'{args.data_root}/images/' + cfg.data.val.ann_file = f'{args.data_root}/annotations/mpii_val.json' + cfg.data.val.img_prefix = f'{args.data_root}/images/' + cfg.data.test.ann_file = f'{args.data_root}/annotations/mpii_test.json' + cfg.data.test.img_prefix = f'{args.data_root}/images/' + + # set cudnn_benchmark + if cfg.get('cudnn_benchmark', False): + torch.backends.cudnn.benchmark = True + + if args.resume_from is not None: + cfg.resume_from = args.resume_from + + cfg.num_of_npus = args.npus + if args.npu_ids is not None: + cfg.npu_ids = args.npu_ids + torch.npu.set_device(cfg.npu_ids[0]) + else: + cfg.npu_ids = range(1) if args.npus is None else range(args.npus) + + if args.autoscale_lr: + cfg.optimizer['lr'] = cfg.optimizer['lr'] * len(cfg.npu_ids) / 8 + + # init distributed env first, since logger depends on the dist info. + if args.launcher == 'none': + distributed = False + else: + distributed = True + init_dist(args.launcher, **cfg.dist_params) + + # create work_dir + mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) + + # init the logger before other steps + timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime()) + log_file = osp.join(cfg.work_dir, f'{timestamp}.log') + logger = get_root_logger(log_file=log_file, log_level=cfg.log_level) + + # init the meta dict to record some important information such as + # environment info and seed, which will be logged + meta = dict() + # log env info + env_info_dict = collect_env() + env_info = '\n'.join([(f'{k}: {v}') for k, v in env_info_dict.items()]) + dash_line = '-' * 60 + '\n' + logger.info('Environment info:\n' + dash_line + env_info + '\n' + + dash_line) + meta['env_info'] = env_info + + # log some basic info + logger.info(f'Distributed training: {distributed}') + logger.info(f'Config:\n{cfg.pretty_text}') + + # set random seeds + if (args.seed != 0): + logger.info(f'Set random seed to {args.seed}, ' + f'deterministic: {args.deterministic}') + set_random_seed(args.seed, deterministic=args.deterministic) + cfg.seed = args.seed + meta['seed'] = args.seed + else: + cfg.seed = None + meta['seed'] = None + + model = build_posenet(cfg.model) + datasets = [build_dataset(cfg.data.train)] + + if len(cfg.workflow) == 2: + val_dataset = copy.deepcopy(cfg.data.val) + val_dataset.pipeline = cfg.data.train.pipeline + datasets.append(build_dataset(val_dataset)) + + if cfg.checkpoint_config is not None: + # save mmpose version, config file content + # checkpoints as meta data + cfg.checkpoint_config.meta = dict( + mmpose_version=__version__ + get_git_hash(digits=7), + config=cfg.pretty_text, + ) + + train_model( + model, + datasets, + cfg, + distributed=distributed, + validate=(not args.no_validate), + timestamp=timestamp, + meta=meta) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/train_1p.sh b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/train_1p.sh new file mode 100644 index 0000000000000000000000000000000000000000..7d33509001c7a2e1d2b7b3b5b4cdc92d670bf9fc --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/train_1p.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +ckpt_interval=$1 +eval_interval=$2 +lr=$3 +warmup_iters=&4 +warmup_ratio=$5 +total_epochs=$6 +work_dir=$7 +data_root=$8 +config=$9 + +PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ +python3 -m torch.distributed.launch --nproc_per_node=1 --master_port=-29500 \ + $(dirname "$0")/train.py $ckpt_interval $eval_interval $lr $warmup_iters \ + $warmup_ratio $total_epochs $work_dir $data_root $config + diff --git a/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/train_modelarts.py b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/train_modelarts.py new file mode 100644 index 0000000000000000000000000000000000000000..d427f6080f90fe065a2cc4d11462819ba1bf5a57 --- /dev/null +++ b/PyTorch/contrib/cv/pose_estimation/Hourglass_for_PyTorch/modelarts/train_modelarts.py @@ -0,0 +1,125 @@ +# coding: utf-8 +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +import argparse +import glob +import os +import subprocess +import ast +import moxing as mox +from selfexport import pth2onnx + + +_CACHE_ROOT_URL = os.path.realpath( + os.path.join(os.path.dirname(os.path.realpath(__file__)), os.path.pardir)) +_CACHE_TRAIN_OUT_URL = os.path.join(_CACHE_ROOT_URL, 'output') +_CACHE_TRAIN_DATA_URL = os.path.join(_CACHE_ROOT_URL, 'data') + +def parse_args(): + parser = argparse.ArgumentParser(description="train hourglass") + parser.add_argument('--train_url', type=str, default='', + help='the path model saved') + parser.add_argument('--data_url', type=str, default='', + help='the training data') + parser.add_argument('--ckpt_interval', type=int, default=1) + parser.add_argument('--eval_interval', type=int, default=5) + parser.add_argument('--lr', type=float, default=5e-5) + parser.add_argument('--warmup_iters', type=int, default=500) + parser.add_argument('--warmup_ratio', type=float, default=0.01) + parser.add_argument('--total_epochs', type=int, default=1) + parser.add_argument('--onnx', default=True, type=ast.literal_eval, + help="convert pth model to onnx") + args = parser.parse_args() + + return args + + +def dos2unix(pattern): + file_list = glob.glob(pattern) + try: + subprocess.run(['dos2unix', *file_list], shell=False, check=True) + except Exception as exp: + print(f"run dos2unix failed, {exp}") + raise exp + + +def find_latest_pth_file(pth_save_dir): + pth_pattern = os.path.join(pth_save_dir, '*.pth') + pth_list = glob.glob(pth_pattern) + if not pth_list: + print(f"Cant't found pth in {pth_save_dir}") + exit() + pth_list.sort(key=os.path.getmtime) + print("==================== %s will be exported to .onnx model next! ====================" % pth_list[-1]) + return os.path.join(pth_list[-1]) + + +def main(): + args = parse_args() + print("Training setting args:", args) + os.makedirs(_CACHE_TRAIN_OUT_URL, mode=0o750, exist_ok=True) + os.makedirs(_CACHE_TRAIN_DATA_URL, mode=0o750, exist_ok=True) + mox.file.copy_parallel(args.data_url, _CACHE_TRAIN_DATA_URL) + + root_dir = _CACHE_ROOT_URL + os.chdir(root_dir) + dos2unix("modelarts/*.sh") + + cfg_script = os.path.join(root_dir, + 'mmpose-master/configs/top_down/hourglass/mpii/hourglass52_mpii_384x384.py') + + train_script = os.path.join(root_dir, 'modelarts/train_1p.sh') + if os.path.islink(train_script): + raise Exception(f"{train_script} shouldn't be a link ") + os.chmod(train_script, 0o500) + + cmd = [ + train_script, + f'--ckpt_interval={args.ckpt_interval}', + f'--eval_interval={args.eval_interval}', + f'--lr={args.lr}', + f'--warmup_iters={args.warmup_iters}', + f'--warmup_ratio={args.warmup_ratio}', + f'--total_epochs={args.total_epochs}', + f'--work_dir={_CACHE_TRAIN_OUT_URL}', + f'--data_root={_CACHE_TRAIN_DATA_URL}', + f'--config={cfg_script}', + ] + + try: + cp = subprocess.run(cmd, shell=False, check=True) + if cp.returncode != 0: + print("run train script failed") + exit(1) + + print("train success") + + if args.onnx: + latest_pth_file = find_latest_pth_file(_CACHE_TRAIN_OUT_URL) + pth2onnx(cfg_file=cfg_script, + pth_file=latest_pth_file, + output_file=os.path.join(_CACHE_TRAIN_OUT_URL, 'hourglass.onnx'), + input_shape=[32, 3, 384, 384]) + + except Exception as exp: + print(f"run {cmd} failed, {exp}") + raise exp + finally: + mox.file.copy_parallel(_CACHE_TRAIN_OUT_URL, args.train_url) + + +if __name__ == '__main__': + main()