車線検出 (2) - MNN を使用して PINet を展開する

目次

1. MNN の紹介

二、MNN編

3. MNN は PINet モデルを展開します

onnx への pytorch

onnx から mnn

MNN 展開

1. MNN の紹介

                アリがオープンソース化した軽量なディープニューラルネットワークエンジンで、ディープラーニングの推論とトレーニングをサポートし、サーバー/パソコン/携帯電話/組み込み機器に適しています。現在、MNN は、Alibaba のモバイル Taobao、モバイル Tmall、Youku などの 30 以上のアプリで使用されており、ライブ ブロードキャスト、ショート ビデオ、検索レコメンデーション、製品画像検索、インタラクティブ マーケティング、権利配布、セキュリティ リスク管理などのシナリオをカバーしています。 . githubアドレスhttps://github.com/alibaba/MNN

二、MNN編

        Linux プラットフォームでのコンパイル プロセスは次のとおりです。

1.依存関係のインストール

  • cmake (3.10 以降)
  • プロトブフ (3.0+)
    • protobuf ライブラリと protobuf コンパイラを指します。バージョン番号は、protoc --version を使用して出力されます。
    • 一部の Linux ディストリビューションでは、これら 2 つのパッケージは別々にリリースされ、手動でインストールする必要があります
    • Ubuntu は2 つのパッケージlibprotobuf-dev とは 別にインストールする必要があります protobuf-compiler
    • sudo apt install libprotobuf-dev
    • sudo apt install protobuf-compiler
    • を使用して Mac OS にbrew install protobuf インストールする
  • C++ コンパイラ
    • GCC または Clang のいずれかを使用できます (macOS を個別にインストールする必要はありません。Xcode が付属しています)。
      • GCC 推奨バージョン 4.9 以降
        • 一部のディストリビューションでは、GCC (GNU C コンパイラ) と G++ (GNU C++ コンパイラは別々にインストールされます)。
        • また、Ubuntuを例にとると、インストールしgccg++ 
      • Clang はバージョン 3.9 以降を推奨します
  • zlib

2. MNN のコードをダウンロードし、解凍後、次のようにします。

mkdir -p ビルド/インストール

CDビルド

cmake .. -DMNN_OPENCL=true -DMNN_SEP_BUILD=false -DMNN_BUILD_CONVERTER=true -DMNN_BUILD_TORCH=true -DMNN_BUILD_DEMO=true -DMNN_BUILD_BENCHMARK=true -DMNN_BUILD_TOOLS=true -DCMAKE_INSTALL_PREFIX=./install

-j4 を作る

インストールする

        コンパイルされたヘッダー ファイルとライブラリ ファイルは、インストール ディレクトリにあります。

3. MNN は PINet モデルを展開します

        まず、モデルを mnn で定義された形式に変換する必要があり、プロセスは pytorch-onnx-mnn です。

onnx への pytorch

        PINet コードをダウンロードして、pytorch の動作環境を構成します。コード ベースの onnx_converter.py は onnx モデルを変換する関数を提供し、onnx_inference.py は onnx 推論を使用したデモを提供します。onnx モデルを取得するには、onnx_conveter.py を実行します。

onnx から mnn

./MNNConvert -f ONNX --modelFile pinet_v2.onnx --MNNModel pinet_v2.mnn --bizCode biz

        これにより、 pinet_v2.mnn モデルが作成されます。

MNN 展開

        関連するデプロイについては onnx_inference.py と mnn の API インターフェイスを参照してください。以下の点に注意する必要があります。

1) 入力画像のフォーマット (正規化されているかどうか); ここでは、デモの例によると、画像フォーマットは BGR であり、[0,1] に正規化されていることがわかります。

2) Netron を使用してネットワークを表示し、入力ノードと出力ノードの名前を取得し、それに応じて入力と出力のテンソルを取得できます。

3) 入力および出力テンソルのデータ形式。MNN の公式ドキュメントによると、内部形式が明確でない場合は、データにアクセスする前に、入力および出力を指定された形式のテンソルに明示的に変換することをお勧めします。

#include <iostream>
#include <opencv2/opencv.hpp>

#include <stdio.h>
#include <MNN/ImageProcess.hpp>
#define MNN_OPEN_TIME_TRACE
#include <algorithm>
#include <fstream>
#include <functional>
#include <memory>
#include <sstream>
#include <vector>
#include <MNN/MNNDefine.h>
#include <MNN/expr/Expr.hpp>
#include <MNN/expr/ExprCreator.hpp>
#include <MNN/AutoTime.hpp>
#include <MNN/Interpreter.hpp>


using namespace MNN;
using namespace MNN::CV;
using namespace MNN::Express;


int main(int argc, char** argv)
{
	std::shared_ptr<Interpreter> net(Interpreter::createFromFile("pinet_v2.mnn"));
	//net->setCacheFile(".tempcache");
	//net->setSessionMode(Interpreter::Session_Debug);
	//net->setSessionMode(Interpreter::Session_Resize_Defer);


	ScheduleConfig config;
	config.numThread = 1;
	config.type = MNN_FORWARD_CPU;
	config.backupType = MNN_FORWARD_OPENCL;

	BackendConfig backendConfig;
	backendConfig.precision = static_cast<MNN::BackendConfig::PrecisionMode>(BackendConfig::Precision_Low);
	config.backendConfig = &backendConfig;

	auto session = net->createSession(config);
	auto input = net->getSessionInput(session, NULL);

	std::vector<int> shape = input->shape();
	std::vector<int> nhwc_shape{ 1, shape[2], shape[3], shape[1] };
	auto nhwc_tensor = new Tensor(input, MNN::Tensor::TENSORFLOW);
	cv::Mat img = cv::imread("3.jpg");
	cv::Mat img_float;
	cv::Mat resized_img;
	cv::resize(img, resized_img, cv::Size(shape[3], shape[2]));
	resized_img.convertTo(resized_img, CV_32FC3);
	resized_img = resized_img / 255.f;

	memcpy(nhwc_tensor->host<float>(), resized_img.data, nhwc_tensor->size());
	input->copyFromHostTensor(nhwc_tensor);

	MNN::Timer time;
	time.reset();
	net->runSession(session);
	MNN_PRINT("use time %f ms\n", time.durationInUs()/ 1000.f);


	auto offset_output = net->getSessionOutput(session, "2830");
	auto nchw_offset_output = new Tensor(offset_output, Tensor::CAFFE);
	offset_output->copyToHostTensor(nchw_offset_output);

	auto feature_output = net->getSessionOutput(session, "2841");
	auto nchw_feature_output = new Tensor(feature_output, Tensor::CAFFE);
	feature_output->copyToHostTensor(nchw_feature_output);

	auto confidence_output = net->getSessionOutput(session, "input.1560");
	auto nchw_confidence_output = new Tensor(confidence_output, Tensor::CAFFE);
	confidence_output->copyToHostTensor(nchw_confidence_output);
	shape = confidence_output->shape();


	// get lines

	std::vector<std::vector<cv::Point>> lines_predicted, lines_final;
	std::vector<std::vector<float>> line_features;

	float width_scale_factor = img.cols / shape[3];
	float height_scale_factor = img.rows / shape[2];


	float* confidence_buf = nchw_confidence_output->host<float>();
	float* feature_buf = nchw_feature_output->host<float>();
	float* offset_buf = nchw_offset_output->host<float>();

	float point_threshold = 0.96;
	float instance_threshold = 0.08;


	for (int h = 0; h < shape[2]; h++)
	{
		for (int w = 0; w < shape[3]; w++)
		{
			int idx = h * shape[3] + w;
			float confidence = confidence_buf[idx];
			if (confidence < point_threshold)
				continue;

			float offset_x = offset_buf[idx];
			float offset_y = offset_buf[shape[3] * shape[2] + idx];
			std::vector<float> feature;
			feature.push_back(feature_buf[idx]);
			feature.push_back(feature_buf[shape[3] * shape[2] + idx]);
			feature.push_back(feature_buf[shape[3] * shape[2] * 2 + idx]);
			feature.push_back(feature_buf[shape[3] * shape[2] * 3 + idx]);

			cv::Point2f pt;
			pt.x = (offset_x + w) * width_scale_factor;
			pt.y = (offset_y + h) * height_scale_factor;

			if (pt.x > img.cols - 1 || pt.x < 0 || pt.y > img.rows - 1 || pt.y < 0)
				continue;

			if (lines_predicted.size() == 0)
			{
				line_features.push_back(feature);
				std::vector<cv::Point> line;
				line.push_back(pt);
				lines_predicted.push_back(line);
			}
			else
			{
				int min_feature_idx = -1;
				float min_feature_dis = 10000;
				for (int n = 0; n < line_features.size(); n++)
				{
					float dis = 0;
					dis += (feature[0] - line_features[n][0]) * (feature[0] - line_features[n][0]);
					dis += (feature[1] - line_features[n][1]) * (feature[1] - line_features[n][1]);
					dis += (feature[2] - line_features[n][2]) * (feature[2] - line_features[n][2]);
					dis += (feature[3] - line_features[n][3]) * (feature[3] - line_features[n][3]);

					if (min_feature_dis > dis)
					{
						min_feature_dis = dis;
						min_feature_idx = n;
					}
				}
				if (min_feature_dis < instance_threshold)
				{
					line_features[min_feature_idx][0] = (line_features[min_feature_idx][0] * lines_predicted[min_feature_idx].size()
						+ feature[0]) / (lines_predicted[min_feature_idx].size() + 1);
					line_features[min_feature_idx][1] = (line_features[min_feature_idx][1] * lines_predicted[min_feature_idx].size()
						+ feature[1]) / (lines_predicted[min_feature_idx].size() + 1);
					line_features[min_feature_idx][2] = (line_features[min_feature_idx][2] * lines_predicted[min_feature_idx].size()
						+ feature[2]) / (lines_predicted[min_feature_idx].size() + 1);
					line_features[min_feature_idx][3] = (line_features[min_feature_idx][3] * lines_predicted[min_feature_idx].size()
						+ feature[3]) / (lines_predicted[min_feature_idx].size() + 1);
					lines_predicted[min_feature_idx].push_back(pt);

				}
				else
				{
					line_features.push_back(feature);
					std::vector<cv::Point> line;
					line.push_back(pt);
					lines_predicted.push_back(line);

				}
			}

		}
	}

	delete nchw_confidence_output;
	delete nchw_feature_output;
	delete nchw_offset_output;
	delete nhwc_tensor;
	// draw point

	cv::Mat draw_lines;
	img.copyTo(draw_lines);

	for (int n = 0; n < lines_predicted.size(); n++)
	{
		if (lines_predicted[n].size() < 3)
			continue;
		cv::RNG rng(cv::getTickCount());
		cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		for (int i = 0; i < lines_predicted[n].size(); i++)
			cv::circle(draw_lines, lines_predicted[n][i], 5, color, 3);
		lines_final.push_back(lines_predicted[n]);
	}

	for (int n = 0; n < lines_final.size(); n++)
	{
		cv::Vec4f param;
		cv::fitLine(lines_final[n], param, CV_DIST_HUBER, 0, 0.01, 0.01);
		float vx, vy, x0, y0;
		vx = param[0];
		vy = param[1];
		x0 = param[2];
		y0 = param[3];
		float x1 = x0 + 1000 * vx;
		float y1 = y0 + 1000 * vy;
		x0 = x0 - 1000 * vx;
		y0 = y0 - 1000 * vy;
		cv::line(draw_lines, cv::Point(x0, y0), cv::Point(x1, y1), cv::Scalar(0, 0, 255), 2);

	}

	cv::imwrite("result.jpg", draw_lines);
	return 0;

}

推論効果         

        推論結果は下の図に示されています。

 

 

おすすめ

転載: blog.csdn.net/lwx309025167/article/details/126732027