Compilar MNN en entorno Ubuntu20.04
Preparación del entorno de compilación
- cmake (se recomienda la versión 3.10 o superior)
- protobuf (usar 3.0 o superior)
- gcc (usar 4.9 o superior)
1. instalación gcc
sudo apt actualizar
sudo apt install build-essential
2. hacer la instalación
- Descargue cmake desde el sitio web oficial de cmake, seleccione la versión de Linux para descargar
Dirección de descarga: cmake - Abra el terminal de comando en la carpeta donde se encuentra el código fuente de cmake y descomprima el archivo
tar -zxv -f cmake-3.23.0-rc3.tar.gz
- Ingresa a la carpeta descomprimida y ejecuta
./oreja
Si ocurre el siguiente error, debe instalar libssl-dev y luego ejecutarlo nuevamente después de la instalación./bootstrap
sudo apt-get install libssl-dev
- Compilar y construir cmake
hacer
- instalar cmake
sudo hacer instalar
- Después de la instalación, ingrese
cmake --version
para verificar si la instalación está completa
3. Instalar protobuf
- instalar dependencias
sudo apt-get install autoconf automake libtool curl make g++ unzip libffi-dev -y
- Descargar código fuente de protobuf
Dirección de descarga: protobuf - Descomprimir el paquete comprimido
tar -zxv -f protobuf-cpp-3.20.0-rc-1.tar.gz
- Ingrese a la carpeta descomprimida para generar el archivo de configuración
cd protobuf-3.20.0-rc-1/
./autogen.sh
- Entorno de configuración
./configurar
- Compilar el código fuente
hacer
- Instalar
sudo hacer instalar
- Actualizar biblioteca dinámica
sudo ldconfig
- Ingrese
protoc --version
para ver si la instalación es exitosa
Compilar MNN
- Descarga el código fuente de MNN y descomprímelo
Link de descarga: MNN - descomprimir
descomprimir MNN-master.zip
- Ingresa a la carpeta descomprimida y ejecuta
cd MNN
./esquema/generar.sh
- Compilación local, una vez completada la compilación, la biblioteca dinámica de MNN aparece localmente
mkdir construir && cd construir && cmake … && hacer -j8
compilación de Android
- Descargue e instale NDK en https://developer.android.com/ndk/downloads/, se recomienda usar la última versión estable
- Configure las variables de entorno de NDK en .bashrc o .bash_profile, por ejemplo: export ANDROID_NDK=/Users/username/path/to/android-ndk-r14b
- cd /ruta/a/MNN
- ./esquema/generar.sh
- ./tools/script/get_model.sh (opcional, el modelo solo se requiere para proyectos de demostración). Tenga en cuenta que get_model.sh necesita compilar la herramienta de conversión de modelos por adelantado, consulte aquí.
- proyecto de cd/android
- Compile la biblioteca dinámica armv7: mkdir build_32 && cd build_32 && .../build_32.sh
- Compile la biblioteca dinámica armv8: mkdir build_64 && cd build_64 && .../build_64.sh
implementar mnist
Instalar la biblioteca opencv
La implementación de mnist también requiere la biblioteca opencv, por lo que debe instalar opencv. Puede
instalarlo clonando el código fuente de OpenCV de acuerdo con este artículo. Después de instalar
el blog
, escriba un programa simple de opencv para verificar si está disponible
. Cree un archivo main.cpp y use gcc compilar y ejecutar
g++ main.cpp -o salida `pkg-config --cflags --libs opencv4`
./salida
#include <opencv2/highgui.hpp>
#include "opencv2/imgcodecs/legacy/constants_c.h"
#include "opencv2/imgproc/types_c.h"
#include <iostream>
int main( int argc, char** argv ) {
cv::Mat image;
image = cv::imread("test.jpg" , CV_LOAD_IMAGE_COLOR);
if(! image.data ) {
std::cout << "Could not open or find the image" << std::endl ;
return -1;
}
std::cout << "image wide: "<< image.cols << ",image high: " << image.rows << ",image channels: "<< image.channels() << std::endl;
/* display image
cv::namedWindow( "Display window", cv::WINDOW_AUTOSIZE );
cv::imshow( "Display window", image );
cv::waitKey(0);
*/
size_t y,x;// y is row, x is col
int c; // c is channel
y = x = 250;
c = 2;
// row_ptr is the head point of y row
unsigned char *row_ptr = image.ptr<unsigned char>(y);
// data_ptr points to pixel data
unsigned char *data_ptr = &row_ptr[x * image.channels()];
unsigned char data = data_ptr[c];
// use cv::Mat::at() to get the pixel value
// unsigned char is not printable
// std::cout << std::isprint(data)<<std::isprint(image.at<cv::Vec3b>(y,x)[c]) << std::endl;
std::cout << "pixel value at y, x ,c"<<static_cast<unsigned>(image.at<cv::Vec3b>(y,x)[c]) << std::endl;
return 0;
}
Si ocurre la siguiente situación durante la operación, significa que la ruta de la biblioteca no está configurada correctamente
.
sudo vi /etc/ld.so.conf.d/opencv.conf
en el directorio /etc/ld.so.conf.d/ sudo vim /etc/ld.so.conf.d/opencv.conf
Escribe dos líneas:
/usr/local/lib
~/opencv_build/opencv/build/lib (aquí se refiere a la lib debajo de la ruta opencv que instaló)
Guarde y salga, ejecute sudo ldconfig
, resuelva el problema
y vuelva a ejecutar ./output
para generar la información correcta, lo que indica que la instalación de opencv se realizó correctamente
implementar mnist
Cree un archivo c ++, escriba el programa de acuerdo con el proceso de razonamiento del documento oficial de MNN, la dirección de descarga del modelo mnn
#include "Backend.hpp"
#include "Interpreter.hpp"
#include "MNNDefine.h"
#include "Interpreter.hpp"
#include "Tensor.hpp"
#include <math.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <stdio.h>
using namespace MNN;
using namespace cv;
int main(void)
{
// 填写自己的测试图像和mnn模型文件路径
std::string image_name = "test.jpg";
const char* model_name = "mnist.mnn";
// 一些任务调度中的配置参数
int forward = MNN_FORWARD_CPU;
// int forward = MNN_FORWARD_OPENCL;
int precision = 2;
int power = 0;
int memory = 0;
int threads = 1;
int INPUT_SIZE = 28;
cv::Mat raw_image = cv::imread(image_name.c_str());
//imshow("image", raw_image);
int raw_image_height = raw_image.rows;
int raw_image_width = raw_image.cols;
cv::Mat image;
cv::resize(raw_image, image, cv::Size(INPUT_SIZE, INPUT_SIZE));
// 1. 创建Interpreter, 通过磁盘文件创建: static Interpreter* createFromFile(const char* file);
std::shared_ptr<Interpreter> net(Interpreter::createFromFile(model_name));
MNN::ScheduleConfig config;
// 2. 调度配置,
// numThread决定并发数的多少,但具体线程数和并发效率,不完全取决于numThread
// 推理时,主选后端由type指定,默认为CPU。在主选后端不支持模型中的算子时,启用由backupType指定的备选后端。
config.numThread = threads;
config.type = static_cast<MNNForwardType>(forward);
MNN::BackendConfig backendConfig;
// 3. 后端配置
// memory、power、precision分别为内存、功耗和精度偏好
backendConfig.precision = (MNN::BackendConfig::PrecisionMode)precision;
backendConfig.power = (MNN::BackendConfig::PowerMode) power;
backendConfig.memory = (MNN::BackendConfig::MemoryMode) memory;
config.backendConfig = &backendConfig;
// 4. 创建session
auto session = net->createSession(config);
net->releaseModel();
clock_t start = clock();
// preprocessing
image.convertTo(image, CV_32FC3);
image = image / 255.0f;
// 5. 输入数据
// wrapping input tensor, convert nhwc to nchw
std::vector<int> dims{
1, INPUT_SIZE, INPUT_SIZE, 3};
auto nhwc_Tensor = MNN::Tensor::create<float>(dims, NULL, MNN::Tensor::TENSORFLOW);
auto nhwc_data = nhwc_Tensor->host<float>();
auto nhwc_size = nhwc_Tensor->size();
::memcpy(nhwc_data, image.data, nhwc_size);
std::string input_tensor = "data";
// 获取输入tensor
// 拷贝数据, 通过这类拷贝数据的方式,用户只需要关注自己创建的tensor的数据布局,
// copyFromHostTensor会负责处理数据布局上的转换(如需)和后端间的数据拷贝(如需)。
auto inputTensor = net->getSessionInput(session, nullptr);
inputTensor->copyFromHostTensor(nhwc_Tensor);
// 6. 运行会话
net->runSession(session);
// 7. 获取输出
std::string output_tensor_name0 = "dense1_fwd";
// 获取输出tensor
MNN::Tensor *tensor_scores = net->getSessionOutput(session, output_tensor_name0.c_str());
MNN::Tensor tensor_scores_host(tensor_scores, tensor_scores->getDimensionType());
// 拷贝数据
tensor_scores->copyToHostTensor(&tensor_scores_host);
// post processing steps
auto scores_dataPtr = tensor_scores_host.host<float>();
// softmax
float exp_sum = 0.0f;
for (int i = 0; i < 10; ++i)
{
float val = scores_dataPtr[i];
exp_sum += val;
}
// get result idx
int idx = 0;
float max_prob = -10.0f;
for (int i = 0; i < 10; ++i)
{
float val = scores_dataPtr[i];
float prob = val / exp_sum;
if (prob > max_prob)
{
max_prob = prob;
idx = i;
}
}
printf("the result is %d\n", idx);
return 0;
}
Escriba CMakeLists.txt, preste atención para reemplazar la dirección de MNN dentro con la dirección de MNN compilada por usted mismo
cmake_minimum_required(VERSION 3.10)
project(mnist)
set(CMAKE_CXX_STANDARD 11)
find_package(OpenCV REQUIRED)
set(MNN_DIR /home/chen/MNN)
include_directories(${MNN_DIR}/include)
include_directories(${MNN_DIR}/include/MNN)
include_directories(${MNN_DIR}/tools)
include_directories(${MNN_DIR}/tools/cpp)
include_directories(${MNN_DIR}/source)
include_directories(${MNN_DIR}/source/backend)
include_directories(${MNN_DIR}/source/core)
LINK_DIRECTORIES(${MNN_DIR}/build)
add_executable(mnist main.cpp)
target_link_libraries(mnist -lMNN ${OpenCV_LIBS})
compilar
cmake. Después de completar
la compilación de make
, aparecerá el archivo ejecutable mnist. Ingrese y./mnist
ejecute, y podrá ver que los dígitos escritos a mano se pueden predecir con éxito.