[Matting] MODNet: modelo de matting de retrato em tempo real - implantação quantitativa NCNN C++

Links Relacionados:

[Matting] MODNet: modelo de matting de retrato em tempo real - implantação onnx python

[Matting] MODNet: Notas de modelo de fosqueamento de retrato em tempo real

[Matting] MODNet: modelo de matting de retrato em tempo real - implantação onnx C++

MODNet é um modelo Matting leve. O modelo onnx do MODNet foi implantado usando python antes. Neste capítulo, o NCNN será usado para implantar o MODNet. Além disso, o modelo é quantizado estaticamente para reduzir seu tamanho em 1/4 do original modelo . O efeito fosco é o seguinte:

O código completo e os pesos necessários estão vinculados no final do artigo.


1. Compilação NCNN

Para etapas específicas, consulte: Tutorial de compilação oficial

1. Compilar protobuf

下载protobuf:https://github.com/google/protobuf/archive/v3.4.0.zip

Abra a ferramenta de linha de comando x64 Native Tools for VS 2017 no menu Iniciar (versões mais avançadas também estão disponíveis, consegui com 2022) e compile o protobuf.

cd <protobuf-root-dir>
mkdir build
cd build
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%cd%/install -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF ../cmake
nmake
nmake install

2. Compilar NCNN

Clone o repositório NCNN:

git clone https://github.com/Tencent/ncnn.git

Compile o NCNN (não uso Vulkan aqui, consulte o tutorial oficial se necessário), substitua o caminho no comando pelo seu próprio caminho:

cd <ncnn-root-dir>
mkdir -p build
cd build
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%cd%/install -DProtobuf_INCLUDE_DIR=<protobuf-root-dir>/build/install/include -DProtobuf_LIBRARIES=<protobuf-root-dir>/build/install/lib/libprotobuf.lib -DProtobuf_PROTOC_EXECUTABLE=<protobuf-root-dir>/build/install/bin/protoc.exe -DNCNN_VULKAN=OFF.. -DOpenCV_DIR=C:/opencv/opencv/build
nmake
nmake install

2. Implantação de NCNN

1. Ambiente

processador do windows

opencv 4.5.5

estúdio visual 2019

2, modelo onnx para ncnn

Primeiro converta o modelo onnx simplificado obtido acima para o modelo ncnn (obtenha 2 arquivos com sufixos param e bin), preste atenção na conversão sem relatar um erro, ou o carregamento subsequente falhará:

O modelo convertido pode ser baixado aqui: Portal

../ncnn/build/tools/onnx/onnx2ncnn simple_modnet.onnx simple_modnet.param simple_modnet.bin 

3. Código C++

Estrutura do código:

 Código MODNet.h:

#pragma once
#include <string>
#include "net.h"
#include <opencv.hpp>
#include "time.h"


class MODNet
{

private:
	std::string param_path;
	std::string bin_path;
	std::vector<int> input_shape;
	ncnn::Net net;

	const float norm_vals[3] = { 1 / 177.5, 1 / 177.5, 1 / 177.5 };
	const float mean_vals[3] = { 175.5, 175.5, 175.5 };

	cv::Mat normalize(cv::Mat& image);
public:
	MODNet() = delete;
	MODNet(const std::string param_path, const std::string bin_path, std::vector<int> input_shape);
	~MODNet();

	cv::Mat predict_image(cv::Mat& image);
	void predict_image(const std::string& src_image_path, const std::string& dst_path);

	void predict_camera();
};

 Código MODNet.cpp:

#include "MODNet.h"


MODNet::MODNet(const std::string param_path, const std::string bin_path, std::vector<int> input_shape)
	:param_path(param_path), bin_path(bin_path), input_shape(input_shape) {
	net.load_param(param_path.c_str());
	net.load_model(bin_path.c_str());
}


MODNet::~MODNet() {
	net.clear();
}


cv::Mat MODNet::normalize(cv::Mat& image) {
	std::vector<cv::Mat> channels, normalized_image;
	cv::split(image, channels);

	cv::Mat r, g, b;
	b = channels.at(0);
	g = channels.at(1);
	r = channels.at(2);
	b = (b / 255. - 0.5) / 0.5;
	g = (g / 255. - 0.5) / 0.5;
	r = (r / 255. - 0.5) / 0.5;

	normalized_image.push_back(r);
	normalized_image.push_back(g);
	normalized_image.push_back(b);

	cv::Mat out = cv::Mat(image.rows, image.cols, CV_32F);
	cv::merge(normalized_image, out);
	return out;
}


cv::Mat MODNet::predict_image(cv::Mat& image) {
	cv::Mat rgbImage;
	cv::cvtColor(image, rgbImage, cv::COLOR_BGR2RGB);
	ncnn::Mat in = ncnn::Mat::from_pixels_resize(rgbImage.data, ncnn::Mat::PIXEL_RGB, image.cols, image.rows, input_shape[3], input_shape[2]);
	in.substract_mean_normalize(mean_vals, norm_vals);
	ncnn::Extractor ex = net.create_extractor();
	ex.set_num_threads(4);
	ex.input("input", in);
	ncnn::Mat out;
	ex.extract("output", out);

	cv::Mat mask(out.h, out.w, CV_8UC1);
	const float* probMap = out.channel(0);

	for (int i{ 0 }; i < out.h; i++) {
		for (int j{ 0 }; j < out.w; ++j) {
			mask.at<uchar>(i, j) = probMap[i * out.w + j] > 0.5 ? 255 : 0;
		}
	}
	cv::resize(mask, mask, cv::Size(image.cols, image.rows), 0, 0);
	cv::Mat segFrame;
	cv::bitwise_and(image, image, segFrame, mask = mask);
	return segFrame;
}


void MODNet::predict_image(const std::string& src_image_path, const std::string& dst_path) {
	cv::Mat image = cv::imread(src_image_path);
	cv::Mat segFrame = predict_image(image);
	cv::imwrite(dst_path, segFrame);
}


void MODNet::predict_camera() {
	cv::Mat frame;
	cv::VideoCapture cap;
	int deviceID{ 0 };
	int apiID{ cv::CAP_ANY };
	cap.open(deviceID, apiID);
	if (!cap.isOpened()) {
		std::cout << "Error, cannot open camera!" << std::endl;
		return;
	}
	//--- GRAB AND WRITE LOOP
	std::cout << "Start grabbing" << std::endl << "Press any key to terminate" << std::endl;
	int count{ 0 };
	clock_t start{ clock() }, end{ 0 };
	double fps{ 0 };
	for (;;)
	{
		// wait for a new frame from camera and store it into 'frame'
		cap.read(frame);
		// check if we succeeded
		if (frame.empty()) {
			std::cout << "ERROR! blank frame grabbed" << std::endl;
			break;
		}
		cv::Mat segFrame = predict_image(frame);

		// fps
		++count;
		end = clock();
		fps = count / (float(end - start) / CLOCKS_PER_SEC);
		if (count >= 50) {
			count = 0;  //防止计数溢出
			start = clock();
		}
		std::cout << "FPS: " << fps << "  Seg Image Number: " << count << "   time consume:" << (float(end - start) / CLOCKS_PER_SEC) << std::endl;
		//设置绘制文本的相关参数
		std::string text{ std::to_string(fps) };
		int font_face = cv::FONT_HERSHEY_COMPLEX;
		double font_scale = 1;
		int thickness = 2;
		int baseline;
		cv::Size text_size = cv::getTextSize(text, font_face, font_scale, thickness, &baseline);

		//将文本框居中绘制
		cv::Point origin;
		origin.x = 20;
		origin.y = 20;
		cv::putText(segFrame, text, origin, font_face, font_scale, cv::Scalar(0, 255, 255), thickness, 8, 0);

		// show live and wait for a key with timeout long enough to show images
		imshow("Live", segFrame);
		if (cv::waitKey(5) >= 0)
			break;

	}
	cap.release();
	cv::destroyWindow("Live");
	return;
}

código main.cpp:

#include <opencv.hpp>
#include <iostream>
#include "MODNet.h"
#include <vector>
#include "net.h"
#include "time.h"


int main() {
	std::string param_path{ "onnx_model\\simple_modnet.param" };
	std::string bin_path{ "onnx_model\\simple_modnet.bin" };
	std::vector<int> input_shape{ 1, 3, 512, 512 };
	MODNet model(param_path, bin_path, input_shape);


	// 预测并显示
	cv::Mat image = cv::imread("C:\\Users\\langdu\\Pictures\\test.png");
	cv::Mat segFrame = model.predict_image(image);
	cv::imshow("1", segFrame);
	cv::waitKey(0);

	// 摄像头
	//model.predict_camera();
	return -1;
}

3. Quantização NCNN

Para dispositivos móveis, o tamanho do modelo é muito exigente e é necessário um método eficaz para reduzir seu espaço de armazenamento. A quantização é um método eficaz para reduzir o tamanho do modelo. Para obter informações quantitativas, consulte: [Deep Learning] Quantização do Modelo - Notas/Experiências

O método de quantização estática é usado aqui e a comparação do tamanho do modelo após a quantização:

bin (KB) param(KB)
Antes da quantificação 25236 22
Após a quantificação 6442 24

Pode-se ver na tabela acima que após a quantização do modelo, seu tamanho é apenas cerca de 1/4 do tamanho original (haverá uma certa perda de precisão na previsão). Vamos iniciar o tutorial quantitativo e consultar o tutorial oficial .

1. Otimização do modelo

Use ncnnoptimize (no diretório build\tools) para otimizar o modelo.

./ncnnoptimize simple_modnet.param simple_modnet.bin quantize_modnet.param quantize_modnet 0

2. Crie uma tabela de calibração

Quando o ncnn cria a tabela de calibração, os parâmetros de média e norma podem ser modificados aqui . Além disso, observe que as configurações de pixel são as mesmas do repositório oficial do MODNet, que é o BGR. As imagens usadas para calibração são armazenadas na pasta de imagens.

find images/ -type f > imagelist.txt
./ncnn2table quantize_modnet.param quantize_modnet.bin imagelist.txt modnet.table mean=[177.5, 177.5, 177.5] norm=[0.00784, 0.00784, 0.00784] shape=[512, 512, 3] pixel=BGR thread=8 method=kl

3. Quantificação

Use o comando a seguir para obter o modelo int8.

./ncnn2int8 quantize_modnet.param quantize_modnet-opt.bin modnet_int8.param modnet_int8.bin modnet.table

4. Usar

Obtém-se o modelo int8, que é muito simples de usar, basta substituir os caminhos bin e param no código acima pelo caminho do modelo int8 gerado.

5. Resultados de previsão após a quantificação

Primeiro, os resultados de previsão não quantizados são fornecidos:

 Os resultados após a quantização (perda de precisão, previsão completa de sapatos e mais erros de piso):

4. Links Relacionados

1. NCNN implanta todo o código + todos os pesos do modelo

Acho que você gosta

Origin blog.csdn.net/qq_40035462/article/details/123902107
Recomendado
Clasificación