Ncnn demo sdk (#164)

* fix different dims and data sync and fp16 macro

* fix pad is negative bug, but still need debugging info

* split detection_output to dets and labels

* fix clang++ compile bug

* for fp16 compile macro of cmake

* fix pad_val dict input

* fix yolox missing normalization and get int tensor

* about score_threshold

* remove -lstdc++fs for android

* move -pthread for android

* fix link libraries for CLANG++;

* fix clang-format

* for mobileyolov3 conf_thre

* fix lint

* fix bug

* support ncnn vulkan net

* remove unused debugging info;

* Change INFO to DEBUG

* support vulkan precision mapping

* fix pad

* optimize getdetslabels

* remove EVAL_MODE

* ncnn_net support 4-dim data.

* remove FP16 compile options

* remove use_gpu_vulkan compile options

* fix pad.cpp

* fix yapf

* fix clang-format

* rm redundant lines

* fix pad according to mmdet

* add android build docs

* fix lint

* use cpp style string comparision

* fix use after free bug

* Add missing -DBUILD_SHARED_LIBS=OFF for en

* Add missing -DBUILD_SHARED_LIBS=OFF for ZH-CN

* reset img2tensor_impl

* reset img2tensor_impl

* add blank line

* sync android.md docs

* fix some dirs

* fix docs

* update docs

* fix code
pull/278/head
hanrui1sensetime 2022-03-25 23:28:16 +08:00 committed by GitHub
parent e01056cc0a
commit 6948e3d04b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 540 additions and 32 deletions

View File

@ -1 +1 @@
backend_config = dict(type='ncnn')
backend_config = dict(type='ncnn', precision='FP32', use_vulkan=False)

View File

@ -12,19 +12,66 @@ namespace mmdeploy::mmdet {
ResizeBBox::ResizeBBox(const Value& cfg) : MMDetection(cfg) {
if (cfg.contains("params")) {
score_thr_ = cfg["params"].value("score_thr", 0.f);
if (cfg["params"].contains("conf_thr")) {
// for mobilev2yolov3
score_thr_ = cfg["params"].value("conf_thr", 0.f);
} else {
score_thr_ = cfg["params"].value("score_thr", 0.f);
}
min_bbox_size_ = cfg["params"].value("min_bbox_size", 0.f);
}
}
std::vector<Tensor> ResizeBBox::GetDetsLabels(const Value& prep_res, const Value& infer_res) {
std::vector<Tensor> results;
if (infer_res.contains("dets") && infer_res.contains("labels")) {
results.push_back(infer_res["dets"].get<Tensor>());
results.push_back(infer_res["labels"].get<Tensor>());
return results;
} else if (infer_res.contains("detection_output") && (!infer_res.contains("dets")) &&
(!infer_res.contains("labels"))) {
int img_width = prep_res["img_metas"]["img_shape"][2].get<int>();
int img_height = prep_res["img_metas"]["img_shape"][1].get<int>();
auto detection_output = infer_res["detection_output"].get<Tensor>();
auto* detection_output_ptr = detection_output.data<float>();
// detection_output: (1, num_det, 6)
TensorDesc labeldesc = detection_output.desc();
int batch_size = detection_output.shape()[0];
int num_det = detection_output.shape()[1];
labeldesc.shape = {batch_size, num_det};
Tensor labels(labeldesc);
TensorDesc detdesc = detection_output.desc();
detdesc.shape = {batch_size, num_det, 5};
Tensor dets(detdesc);
auto* dets_ptr = dets.data<float>();
auto* labels_ptr = labels.data<float>();
for (int i = 0; i < batch_size * num_det; ++i) {
*labels_ptr++ = detection_output_ptr[0] - 1;
dets_ptr[4] = detection_output_ptr[1];
dets_ptr[0] = detection_output_ptr[2] * img_width;
dets_ptr[1] = detection_output_ptr[3] * img_height;
dets_ptr[2] = detection_output_ptr[4] * img_width;
dets_ptr[3] = detection_output_ptr[5] * img_height;
dets_ptr += 5;
detection_output_ptr += 6;
}
results.push_back(dets);
results.push_back(labels);
return results;
} else {
MMDEPLOY_ERROR("No support for another key of detection results!");
return results;
}
}
Result<Value> ResizeBBox::operator()(const Value& prep_res, const Value& infer_res) {
MMDEPLOY_DEBUG("prep_res: {}\ninfer_res: {}", prep_res, infer_res);
try {
auto dets = infer_res["dets"].get<Tensor>();
auto labels = infer_res["labels"].get<Tensor>();
Tensor dets, labels;
vector<Tensor> outputs = GetDetsLabels(prep_res, infer_res);
dets = outputs[0];
labels = outputs[1];
MMDEPLOY_DEBUG("dets.shape: {}", dets.shape());
MMDEPLOY_DEBUG("labels.shape: {}", labels.shape());
// `dets` is supposed to have 3 dims. They are 'batch', 'bboxes_number'
// and 'channels' respectively
if (!(dets.shape().size() == 3 && dets.data_type() == DataType::kFLOAT)) {
@ -72,7 +119,6 @@ Result<DetectorOutput> ResizeBBox::GetBBoxes(const Value& prep_res, const Tensor
DetectorOutput objs;
auto* dets_ptr = dets.data<float>();
auto* labels_ptr = labels.data<T>();
vector<float> scale_factor;
if (prep_res.contains("scale_factor")) {
from_value(prep_res["scale_factor"], scale_factor);

View File

@ -26,6 +26,8 @@ class ResizeBBox : public MMDetection {
const float* scale_factor, float x_offset, float y_offset,
int ori_width, int ori_height);
std::vector<Tensor> GetDetsLabels(const Value& prep_res, const Value& infer_res);
protected:
constexpr static Device kHost{0, 0};
float score_thr_{0.f};

View File

@ -48,7 +48,7 @@ target_include_directories(${PROJECT_NAME}
$<INSTALL_INTERFACE:include/cpp>
)
target_link_libraries(${PROJECT_NAME} PUBLIC ${SPDLOG_LIB})
if (NOT MSVC)
if (NOT (MSVC OR ANDROID))
target_link_libraries(${PROJECT_NAME} PUBLIC stdc++fs)
endif ()
add_library(mmdeploy::core ALIAS ${PROJECT_NAME})

View File

@ -36,7 +36,7 @@ std::string Stacktrace::to_string() const {
} // namespace mmdeploy
#else
#include <string>
namespace mmdeploy {
struct Stacktrace::Impl {};

View File

@ -12,7 +12,7 @@ endif ()
foreach (MODEL_NAME ${MODEL_NAMES})
set(TARGET_MODEL_NAME mmdeploy_${MODEL_NAME})
mmdeploy_add_module(${TARGET_MODEL_NAME} ${MODEL_NAME}_impl.cpp)
if (NOT MSVC)
if (NOT (MSVC OR ANDROID))
target_link_libraries(${TARGET_MODEL_NAME} PUBLIC stdc++fs)
endif ()
if (${MODEL_NAME} STREQUAL "zip_model")

View File

@ -22,7 +22,9 @@ Result<void> NCNNNet::Init(const Value& args) {
auto& context = args["context"];
device_ = context["device"].get<Device>();
stream_ = context["stream"].get<Stream>();
if (args.contains("use_vulkan")) {
net_.opt.use_vulkan_compute = args["use_vulkan"].get<bool>();
}
if (!device_.is_host()) {
return Status(eNotSupported);
}
@ -30,10 +32,29 @@ Result<void> NCNNNet::Init(const Value& args) {
auto name = args["name"].get<std::string>();
auto model = context["model"].get<Model>();
OUTCOME_TRY(auto config, model.GetModelConfig(name));
auto precision = config.precision;
if (precision == "FP16") {
net_.opt.use_fp16_packed = true;
net_.opt.use_fp16_storage = true;
net_.opt.use_fp16_arithmetic = true;
} else if (precision == "INT8") {
// in android platform, ncnn will automatically start FP16 accelerate.
// In INT8 case, we set fp16 as false explicitly.
net_.opt.use_int8_packed = true;
net_.opt.use_int8_storage = true;
net_.opt.use_int8_arithmetic = true;
net_.opt.use_fp16_packed = false;
net_.opt.use_fp16_storage = false;
net_.opt.use_fp16_arithmetic = false;
} else {
// in android platform, ncnn will automatically start FP16 accelerate.
// In FP32 case, we set fp16 as false explicitly.
net_.opt.use_fp16_packed = false;
net_.opt.use_fp16_storage = false;
net_.opt.use_fp16_arithmetic = false;
}
OUTCOME_TRY(params_, model.ReadFile(config.net));
OUTCOME_TRY(weights_, model.ReadFile(config.weights));
register_mmdeploy_custom_layers(net_);
OUTCOME_TRY(ncnn_status(net_.load_param_mem(params_.c_str())));
@ -57,7 +78,6 @@ Result<void> NCNNNet::Init(const Value& args) {
x,
});
}
return success();
}
@ -91,10 +111,24 @@ Result<void> NCNNNet::Forward() {
OUTCOME_TRY(ncnn_status(extractor.extract(output_indices_[i], outputs[i])));
auto& tensor = output_tensors_[i];
auto shape = outputs[i].shape();
tensor.Reshape({1, shape.w, shape.h, shape.c});
if (outputs[i].dims == 1) {
tensor.Reshape({1, shape.w});
} else if (outputs[i].dims == 2) {
tensor.Reshape({1, shape.h, shape.w});
} else if (outputs[i].dims == 3) {
tensor.Reshape({1, shape.d, shape.h, shape.w});
} else {
// for dim==4 case and blank image.
tensor.Reshape({1, shape.c, shape.d, shape.h, shape.w});
}
// tensor.Reshape({1, shape.c, shape.h, shape.w});
// ncnn Mat may be padded, flatten to avoid that
auto flattened = outputs[i].reshape(shape.w * shape.h * shape.c);
OUTCOME_TRY(tensor.CopyFrom(flattened.data, stream_));
auto flattened = outputs[i].reshape(shape.c * shape.h * shape.w);
// if ((shape.c * shape.h * shape.w) > 0)
if (outputs[i].dims > 0) {
OUTCOME_TRY(tensor.CopyFrom(flattened.data, stream_));
}
OUTCOME_TRY(stream_.Wait());
}
return success();
}

View File

@ -22,7 +22,17 @@ PadImpl::PadImpl(const Value& args) : TransformImpl(args) {
}
arg_.size_divisor = args.value("size_divisor", 1);
arg_.pad_val = args.value("pad_val", 0.0f);
if (args.contains("pad_val")) {
if (args["pad_val"].is_number()) {
arg_.pad_val = args["pad_val"].get<float>();
} else if (args["pad_val"].contains("img")) {
arg_.pad_val = args["pad_val"]["img"][0].get<float>();
} else {
throw std::invalid_argument("args must be number or img dict");
}
} else {
arg_.pad_val = 0.0f;
}
arg_.pad_to_square = args.value("pad_to_square", false);
arg_.padding_mode = args.value("padding_mode", std::string("constant"));
}
@ -31,43 +41,39 @@ Result<Value> PadImpl::Process(const Value& input) {
MMDEPLOY_DEBUG("input: {}", to_json(input).dump(2));
Value output = input;
auto img_fields = GetImageFields(input);
for (auto& key : img_fields) {
Tensor output_tensor;
auto tensor = input[key].get<Tensor>();
assert(tensor.desc().shape.size() == 4);
assert(tensor.desc().shape[0] == 1);
assert(tensor.desc().shape[3] == 3 || tensor.desc().shape[3] == 1);
int height = tensor.desc().shape[1];
int width = tensor.desc().shape[2];
if (arg_.pad_to_square) {
int max_size = std::max(tensor.desc().shape[1], tensor.desc().shape[2]);
std::array padding{0, 0, max_size - width, max_size - height};
OUTCOME_TRY(output_tensor, PadImage(tensor, padding));
output["pad_fixed_size"].push_back(max_size);
output["pad_fixed_size"].push_back(max_size);
} else if (arg_.size[0] != 0 && arg_.size[1] != 0) {
output_tensor = tensor;
std::array padding{0, 0, arg_.size[1] - width, arg_.size[0] - height};
OUTCOME_TRY(output_tensor, PadImage(tensor, padding));
output["pad_fixed_size"].push_back(arg_.size[0]);
output["pad_fixed_size"].push_back(arg_.size[1]);
} else if (arg_.size_divisor != 1) {
auto pad_h = (height + arg_.size_divisor - 1) / arg_.size_divisor * arg_.size_divisor;
auto pad_w = (width + arg_.size_divisor - 1) / arg_.size_divisor * arg_.size_divisor;
std::array padding{0, 0, pad_w - width, pad_h - height};
OUTCOME_TRY(output_tensor, PadImage(tensor, padding));
output["pad_size_divisor"] = arg_.size_divisor;
output["pad_fixed_size"].push_back(pad_h);
output["pad_fixed_size"].push_back(pad_w);
} else {
std::array padding{0, 0, arg_.size[1] - width, arg_.size[0] - height};
OUTCOME_TRY(output_tensor, PadImage(tensor, padding));
output["pad_fixed_size"].push_back(arg_.size[0]);
output["pad_fixed_size"].push_back(arg_.size[1]);
output_tensor = tensor;
output["pad_fixed_size"].push_back(height);
output["pad_fixed_size"].push_back(width);
}
output[key] = output_tensor;
for (auto& v : output_tensor.desc().shape) {

View File

@ -0,0 +1,210 @@
# Build for Android
- [Build for Android](#build-for-android)
- [Build From Source](#build-from-source)
- [Install Toolchains](#install-toolchains)
- [Install Dependencies](#install-dependencies)
- [Install Dependencies for SDK](#install-dependencies-for-sdk)
- [Build MMDeploy](#build-mmdeploy)
- [Build Options Spec](#build-options-spec)
- [Build SDK](#build-sdk)
- [Build Demo](#build-demo)
---
MMDeploy provides cross compile for android platform.
Model converter is executed on linux platform, and SDK is executed on android platform.
Here are two steps for android build.
1. Build model converter on linux, please refer to [How to build linux](./linux.md)
2. Build SDK using android toolchain on linux.
This doc is only for how to build SDK using android toolchain on linux.
## Build From Source
### Install Toolchains
- cmake
**Make sure cmake version >= 3.14.0**. If not, you can follow instructions below to install cmake 3.20.0. For more versions of cmake, please refer to [cmake website](https://cmake.org/install).
```bash
sudo apt-get install -y libssl-dev
wget https://github.com/Kitware/CMake/releases/download/v3.20.0/cmake-3.20.0.tar.gz
tar -zxvf cmake-3.20.0.tar.gz
cd cmake-3.20.0
./bootstrap
make
sudo make install
```
- ANDROID NDK 19+
**Make sure android ndk version >= 19.0**. If not, you can follow instructions below to install android ndk r23b. For more versions of android ndk, please refer to [android ndk website](https://developer.android.com/ndk/downloads).
```bash
wget https://dl.google.com/android/repository/android-ndk-r23b-linux.zip
unzip android-ndk-r23b-linux.zip
cd android-ndk-r23b
export NDK_PATH=${PWD}
```
### Install Dependencies
#### Install Dependencies for SDK
You can skip this chapter if only interested in model converter.
<table>
<thead>
<tr>
<th>NAME </th>
<th>INSTALLATION </th>
</tr>
</thead>
<tbody>
<tr>
<td>spdlog </td>
<td>
<pre><code>
git clone -b v1.9.2 https://github.com/gabime/spdlog.git
cd spdlog
export SPDLOG_DIR=${PWD}
mkdir -p build
cd build
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=${SPDLOG_DIR} -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-30 ..
make install
</code></pre>
</td>
</tr>
<tr>
<td>OpenCV<br>(>=3.0) </td>
<td>
<pre><code>
export OPENCV_VERSION=4.5.4
wget https://github.com/opencv/opencv/releases/download/${OPENCV_VERSION}/opencv-${OPENCV_VERSION}-android-sdk.zip
unzip opencv-${OPENCV_VERSION}-android-sdk.zip
export OPENCV_ANDROID_SDK_DIR=${PWD}/OpenCV-android-sdk
</code></pre>
</td>
</tr>
<tr>
<td>ncnn </td>
<td>A high-performance neural network inference computing framework supporting for android.</br>
<b> Now, MMDeploy supports v20211208 and has to use <code>git clone</code> to download it.</b><br>
<pre><code>
git clone -b 20211208 https://github.com/Tencent/ncnn.git
cd ncnn
git submodule update --init
export NCNN_DIR=${PWD}
mkdir -p build
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-30 -DNCNN_VULKAN=ON -DNCNN_DISABLE_EXCEPTION=OFF -DNCNN_DISABLE_RTTI=OFF ..
make install
</code></pre>
</td>
</tr>
</tbody>
</table>
### Build MMDeploy
#### Build Options Spec
<table>
<thead>
<tr>
<th>NAME</th>
<th>VALUE</th>
<th>DEFAULT</th>
<th>REMARK</th>
</tr>
</thead>
<tbody>
<tr>
<td>MMDEPLOY_BUILD_SDK</td>
<td>{ON, OFF}</td>
<td>OFF</td>
<td>Switch to build MMDeploy SDK</td>
</tr>
<tr>
<td>MMDEPLOY_BUILD_SDK_PYTHON_API</td>
<td>{ON, OFF}</td>
<td>OFF</td>
<td>switch to build MMDeploy SDK python package</td>
</tr>
<tr>
<td>MMDEPLOY_BUILD_TEST</td>
<td>{ON, OFF}</td>
<td>OFF</td>
<td>Switch to build MMDeploy SDK unittest cases</td>
</tr>
<tr>
<td>MMDEPLOY_TARGET_DEVICES</td>
<td>{"cpu"}</td>
<td>cpu</td>
<td>Enable target device. <br>If you want use ncnn vulkan accelerate, you still fill <code>{"cpu"}</code> here. Because, vulkan accelerate is only for ncnn net. The other part of inference is still using cpu.</td>
</tr>
<tr>
<td>MMDEPLOY_TARGET_BACKENDS</td>
<td>{"ncnn"}</td>
<td>N/A</td>
<td>Enabling inference engine. <br><b>By default, no target inference engine is set, since it highly depends on the use case.</b><br> Only ncnn backend is supported for android platform now.<br>
After specifying the inference engine, it's package path has to be passed to cmake as follows, <br>
1. <b>ncnn</b>: ncnn. <code>ncnn_DIR</code> is needed.
<pre><code>-Dncnn_DIR=${NCNN_DIR}/build/install/lib/cmake/ncnn</code></pre>
</td>
</tr>
<tr>
<td>MMDEPLOY_CODEBASES</td>
<td>{"mmcls", "mmdet", "mmseg", "mmedit", "mmocr", "all"}</td>
<td>N/A</td>
<td>Enable codebase's postprocess modules. It MUST be set by a semicolon separated list of codebase names. The currently supported codebases are 'mmcls', 'mmdet', 'mmedit', 'mmseg', 'mmocr'. Instead of listing them one by one, you can also pass <code>all</code> to enable them all, i.e., <code>-DMMDEPLOY_CODEBASES=all</code></td>
</tr>
<tr>
<td>BUILD_SHARED_LIBS</td>
<td>{ON, OFF}</td>
<td>ON</td>
<td>Switch to build shared library or static library of MMDeploy SDK. Now you should build static library for android. Bug will be fixed soon.</td>
</tr>
</tbody>
</table>
#### Build SDK
MMDeploy provides a recipe as shown below for building SDK with ncnn as inference engine for android.
- cpu + ncnn
```Bash
cd ${MMDEPLOY_DIR}
mkdir -p build && cd build
cmake .. \
-DMMDEPLOY_BUILD_SDK=ON \
-DOpenCV_DIR=${OPENCV_ANDROID_SDK_DIR}/sdk/native/jni/abi-arm64-v8a \
-Dspdlog_DIR=${SPDLOG_DIR}/lib/cmake/spdlog \
-Dncnn_DIR=${NCNN_DIR}/build/install/lib/cmake/ncnn \
-DMMDEPLOY_TARGET_BACKENDS=ncnn \
-DMMDEPLOY_CODEBASES=all \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-30 \
-DANDROID_CPP_FEATURES="rtti exceptions"
make -j$(nproc) && make install
```
#### Build Demo
```Bash
cd ${MMDEPLOY_DIR}/build/install/example
mkdir -p build && cd build
cmake .. \
-DOpenCV_DIR=${OPENCV_ANDROID_SDK_DIR}/sdk/native/jni/abi-arm64-v8a \
-Dspdlog_DIR=${SPDLOG_DIR}/lib/cmake/spdlog \
-DMMDeploy_DIR=${MMDEPLOY_DIR}/build/install/lib/cmake/MMDeploy \
-DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-30
cmake --build . -- -j$(nproc)
```

View File

@ -0,0 +1,205 @@
# Android 下构建方式
- [Android 下构建方式](#Android 下构建方式)
- [源码安装](#源码安装)
- [安装构建和编译工具链](#安装构建和编译工具链)
- [安装依赖包](#安装依赖包)
- [安装 MMDeploy SDK 依赖](#安装-MMDeploy-SDK-依赖)
- [编译 MMDeploy](#编译-mmdeploy)
- [编译选项说明](#编译选项说明)
- [编译 SDK](#编译-sdk)
- [编译 Demo](#编译-demo)
---
MMDeploy 为 android 平台提供交叉编译的构建方式.
MMDeploy converter 部分在 linux 平台上执行,SDK 部分在 android 平台上执行.
MMDeploy 的交叉编译分为两步:
1. 在 linux 平台上构建 MMDeploy converter. 请根据 [How to build linux](./linux.md) 进行构建.
2. 使用 android 工具链构建 MMDeploy SDK.
本文档仅提供在 linux 平台上使用 android 工具链进行交叉编译构建 MMDeploy SDK 的方法.
## 源码安装
### 安装构建和编译工具链
- cmake
**保证 cmake的版本 >= 3.14.0**. 如果不是,可以参考以下命令安装 3.20.0 版本. 如要获取其他版本,请参考 [这里](https://cmake.org/install)
```bash
sudo apt-get install -y libssl-dev
wget https://github.com/Kitware/CMake/releases/download/v3.20.0/cmake-3.20.0.tar.gz
tar -zxvf cmake-3.20.0.tar.gz
cd cmake-3.20.0
./bootstrap
make
sudo make install
```
- ANDROID NDK 19+
**保证 android ndk 的版本 >= 19.0**. 如果不是,可以参考以下命令安装 r23b 版本. 如要获取其他版本,请参考 [这里](https://developer.android.com/ndk/downloads)
```bash
wget https://dl.google.com/android/repository/android-ndk-r23b-linux.zip
unzip android-ndk-r23b-linux.zip
cd android-ndk-r23b
export NDK_PATH=${PWD}
```
### 安装依赖包
#### 安装 MMDeploy SDK 依赖
如果您只对模型转换感兴趣,那么可以跳过本章节.
<table>
<thead>
<tr>
<th>名称 </th>
<th>安装方式 </th>
</tr>
</thead>
<tbody>
<tr>
<td>spdlog </td>
<td>
<pre><code>
git clone -b v1.9.2 https://github.com/gabime/spdlog.git
cd spdlog
export SPDLOG_DIR=${PWD}
mkdir -p build
cd build
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=${SPDLOG_DIR} -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-30 ..
make install
</code></pre>
</td>
</tr>
<tr>
<td>OpenCV<br>(>=3.0) </td>
<td>
<pre><code>
export OPENCV_VERSION=4.5.4
wget https://github.com/opencv/opencv/releases/download/${OPENCV_VERSION}/opencv-${OPENCV_VERSION}-android-sdk.zip
unzip opencv-${OPENCV_VERSION}-android-sdk.zip
export OPENCV_ANDROID_SDK_DIR=${PWD}/OpenCV-android-sdk
</code></pre>
</td>
</tr>
<tr>
<td>ncnn </td>
<td>ncnn 是支持 android 平台的高效神经网络推理计算框架</br>
<b> 目前, MMDeploy 支持 ncnn 的 20211208 版本, 且必须使用<code>git clone</code> 下载源码的方式安装</b><br>
<pre><code>
git clone -b 20211208 https://github.com/Tencent/ncnn.git
cd ncnn
git submodule update --init
export NCNN_DIR=${PWD}
mkdir -p build
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-30 -DNCNN_VULKAN=ON -DNCNN_DISABLE_EXCEPTION=OFF -DNCNN_DISABLE_RTTI=OFF ..
make install
</code></pre>
</td>
</tr>
</tbody>
</table>
### 编译 MMDeploy
#### 编译选项说明
<table>
<thead>
<tr>
<th>编译选项</th>
<th>取值范围</th>
<th>缺省值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>MMDEPLOY_BUILD_SDK</td>
<td>{ON, OFF}</td>
<td>OFF</td>
<td>MMDeploy SDK 编译开关</td>
</tr>
<tr>
<td>MMDEPLOY_BUILD_TEST</td>
<td>{ON, OFF}</td>
<td>OFF</td>
<td>MMDeploy SDK 的单元测试程序编译开关</td>
</tr>
<tr>
<td>MMDEPLOY_TARGET_DEVICES</td>
<td>{"cpu"}</td>
<td>cpu</td>
<td>设置目标设备. <br>如果您想使用 ncnn 的 vulkan 加速功能, 您仍旧需要在这里填写<code>{"cpu"}</code>参数. 这是因为 vulkan 加速仅用于 ncnn 网络计算内部,推理过程的其他部分仍旧使用 cpu 计算.</td>
</tr>
<tr>
<td>MMDEPLOY_TARGET_BACKENDS</td>
<td>{"ncnn"}</td>
<td>N/A</td>
<td>设置推理后端. <br><b>默认情况下,SDK不设置任何后端, 因为它与应用场景高度相关.</b><br> Android 端目前只支持ncnn一个后端 <br>
构建时,几乎每个后端,都需传入一些路径变量,用来查找依赖包. <br>
1. <b>ncnn</b>: 表示 ncnn. 需要设置<code>ncnn_DIR</code>.
<pre><code>-Dncnn_DIR=${NCNN_DIR}/build/install/lib/cmake/ncnn</code></pre>
</td>
</tr>
<tr>
<td>MMDEPLOY_CODEBASES</td>
<td>{"mmcls", "mmdet", "mmseg", "mmedit", "mmocr", "all"}</td>
<td>N/A</td>
<td>用来设置 SDK 后处理组件,加载 OpenMMLab 算法仓库的后处理功能. 已支持的算法仓库有'mmcls','mmdet','mmedit','mmseg'和'mmocr'. 如果选择多个codebase,中间使用分号隔开. 比如, 'mmcls', 'mmdet', 'mmedit', 'mmseg', 'mmocr'. 也可以通过 <code>all</code> 的方式, 加载所有codebase, 即 <code>-DMMDEPLOY_CODEBASES=all.</code></td>
</tr>
<tr>
<td>BUILD_SHARED_LIBS</td>
<td>{ON, OFF}</td>
<td>ON</td>
<td>MMDeploy SDK 的动态库的编译开关.设置 OFF 时, 编译静态库. 目前 android 端 SDK 仅支持静态库加载, 后续会进行对动态库加载的支持.</td>
</tr>
</tbody>
</table>
#### 编译 SDK
下文展示构建SDK的样例用 ncnn 作为推理引擎。
- cpu + ncnn
```Bash
cd ${MMDEPLOY_DIR}
mkdir -p build && cd build
cmake .. \
-DMMDEPLOY_BUILD_SDK=ON \
-DOpenCV_DIR=${OPENCV_ANDROID_SDK_DIR}/sdk/native/jni/abi-arm64-v8a \
-Dspdlog_DIR=${SPDLOG_DIR}/lib/cmake/spdlog \
-Dncnn_DIR=${NCNN_DIR}/build/install/lib/cmake/ncnn \
-DMMDEPLOY_TARGET_BACKENDS=ncnn \
-DMMDEPLOY_CODEBASES=all \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-30 \
-DANDROID_CPP_FEATURES="rtti exceptions"
make -j$(nproc) && make install
```
#### 编译 Demo
```Bash
cd ${MMDEPLOY_DIR}/build/install/example
mkdir -p build && cd build
cmake .. \
-DOpenCV_DIR=${OPENCV_ANDROID_SDK_DIR}/sdk/native/jni/abi-arm64-v8a \
-Dspdlog_DIR=${SPDLOG_DIR}/lib/cmake/spdlog \
-DMMDeploy_DIR=${MMDEPLOY_DIR}/build/install/lib/cmake/MMDeploy \
-DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-30
cmake --build . -- -j$(nproc)
```

View File

@ -112,6 +112,8 @@ def get_models(deploy_cfg: Union[str, mmcv.Config],
elif backend == Backend.NCNN:
net = onnx_name.replace('.onnx', '.param')
weights = onnx_name.replace('.onnx', '.bin')
if 'precision' in deploy_cfg['backend_config']:
precision = deploy_cfg['backend_config']['precision']
elif backend == Backend.ONNXRUNTIME:
pass
else:
@ -153,13 +155,16 @@ def get_inference_info(deploy_cfg: mmcv.Config, model_cfg: mmcv.Config,
input_names = onnx_config.get('input_names', None)
input_name = input_names[0] if input_names else 'input'
input_map = dict(img=input_name)
return dict(
return_dict = dict(
name=name,
type=type,
module=module,
input=input,
output=output,
input_map=input_map)
if 'use_vulkan' in deploy_cfg['backend_config']:
return_dict['use_vulkan'] = deploy_cfg['backend_config']['use_vulkan']
return return_dict
def get_preprocess(deploy_cfg: mmcv.Config, model_cfg: mmcv.Config):