содержание
Первый шаг — перенести обученный файл .h5 в файл .weight
Третий шаг, скопируйте файл веса, файл категории и файл cfg в папку bin программы C#.
Четвертый шаг, скачать CSharpOpenCv
Четвертый шаг, напишите код C#
Первый шаг — перенести обученный файл .h5 в файл .weight
Инфраструктура TensorFlow + Keras используется для обучения модели, и ее модель параметров выводится в виде файла .h5, который содержит параметры сверточного слоя и слоя пула в результате обучения yolo.
Библиотека OpenCV, в которой модуль Deep Neural Network (dnn) использует платформу DarkNet, модель, импортируемая платформой, представляет собой файл .weights и зависит от архитектуры, определенной в файле .cfg. Итак, если вы хотите использовать модель, обученную предыдущим фреймворком в OpenCV, вам нужнобудет ****йоло.h5 файл преобразован в yolo.weights документ. Выполните следующий код:
from tensorflow.keras.models import load_model
import configparser
import io
from collections import defaultdict
import numpy as np
from yolo import YOLO
def unique_config_sections(config_file):
"""Convert all config sections to have unique names.
Adds unique suffixes to config sections for compability with configparser.
"""
section_counters = defaultdict(int)
output_stream = io.StringIO()
with open(config_file) as fin:
for line in fin:
if line.startswith('['):
section = line.strip().strip('[]')
_section = section + '_' + str(section_counters[section])
section_counters[section] += 1
line = line.replace(section, _section)
output_stream.write(line)
output_stream.seek(0)
return output_stream
def _main():
unique_config_file = unique_config_sections(config_path)
cfg_parser = configparser.ConfigParser()
cfg_parser.read_file(unique_config_file)
# major, minor, revision=[0,2,0] seen=32013312
m_revision=[0,2,0]
seen=[32013312]
# convert to bytes
m_revision_const = np.array(m_revision,dtype=np.int32)
m_revision_bytes=m_revision_const.tobytes()
seen_const=np.array(seen,dtype=np.int64)
seen_bytes=seen_const.tobytes()
print('write revision information\n')
weight_file.write(m_revision_bytes)
weight_file.write(seen_bytes)
# conv2d and batch_normalize layers
b=0
print('start write weights\n')
for section in cfg_parser.sections():
#print('Parsing section {}'.format(section))
if section.startswith('convolutional'):
# get 'convolutional_'
num = int(section.split('_')[-1])+1
# get 'batch_normalize'
batch_normalize = 'batch_normalize' in cfg_parser[section]
# if batch_normalize write it three times and activation='leaky'
if batch_normalize:
# from batch_normalization layer extract bn_weight_list
batch_weight_name = 'batch_normalization_' + str(num-b)
bn_weight_list_layer=model.get_layer(batch_weight_name)
bn_weight_list =bn_weight_list_layer.get_weights()
# from bn_weight_list extract bn_weight and con_bias
conv_bias = bn_weight_list[1]
bn_weight = [bn_weight_list[0], bn_weight_list[2], bn_weight_list[3]]
# from conv2d layer extract conv_weight
conv2d_weight_name = 'conv2d_' + str(num)
# print conv2d_weight_name
print(conv2d_weight_name,'\n')
print(batch_weight_name, '\n')
conv2d_weight_name_layer=model.get_layer(conv2d_weight_name)
# list[ndarray]
conv_weight = conv2d_weight_name_layer.get_weights()
conv_weight=conv_weight[0]
conv_weight = np.transpose(conv_weight, [3, 2, 0, 1])
bias_weight = np.array(conv_bias,dtype=np.float32)
bytes_bias_weight=bias_weight.tobytes()
weight_file.write(bytes_bias_weight)
print(bias_weight.shape,'\n')
# convert bn_weight to bytes then write to file
bn_weight_array=np.array(bn_weight,dtype=np.float32)
bytes_bn_weight=bn_weight_array.tobytes()
weight_file.write(bytes_bn_weight)
print(bn_weight_array.shape,'\n')
conv_weight_array=np.array(conv_weight,dtype=np.float32)
bytes_conv_weight=conv_weight_array.tobytes()
weight_file.write(bytes_conv_weight)
print(conv_weight_array.shape,'\n')
# not existence batch_normalize layers, write it two times
else:
# b is disorder parameter
b+=1
# from conv2d layer extract conv_weight(include conv_bias)
print('\n')
conv2d_weight_name = 'conv2d_' + str(num)
print('disorder',conv2d_weight_name,'\n\n')
conv2d_weight_name_layer = model.get_layer(conv2d_weight_name)
conv_weights =conv2d_weight_name_layer.get_weights()
# extract conv_bias conv2d_weight
conv_bias = conv_weights[-1]
conv_weight = conv_weights[0]
conv_weight=np.array(conv_weight)
# transpose
conv_weight = np.transpose(conv_weight, [3, 2, 0, 1])
# write the file with order conv_bias、conv2d_weight
# conv_bias convert to bytes
bias_weight = np.array(conv_bias,dtype=np.float32)
bytes_bias_weight = bias_weight.tobytes()
weight_file.write(bytes_bias_weight)
print(bias_weight.shape)
# conv_weight convert to bytes
conv_weight_array = np.array(conv_weight,dtype=np.float32)
bytes_conv_weight = conv_weight_array.tobytes()
weight_file.write(bytes_conv_weight)
# pritn the shape
print(conv_weight_array.shape)
weight_file.close()
print("convert success!\n")
if __name__ == '__main__':
model_path = "weight/yolov3.h5" # keras yolov3 h5 model file
config_path = 'yolov3.cfg' # .cfg file path
weight_file = open('yolov3.weights', 'wb') # save darknet yolov3 weights file path
"""
The default keras yolov3 (https://github.com/qqwweee/keras-yolo3/blob/master/train.py)
after trained save with method " model.save_weights(log_dir + 'trained_weights_final.h5')"
it actually only saved weights, below call YOLO(modelpath) will check it's model,
if it without model information, then automatic load model.
"""
yoloobj = YOLO()
model = yoloobj.yolo_model
_main()
После завершения выполнения можно получить файл yolov3.weights.
Второй шаг, изменить файл cfg
Открытымyolov3.cfg, найти внутриyolo(Обратите внимание, что есть 3 места), места, которые необходимо изменить:
1. фильтры = 3 * ( 5 + классы )
2. классы = n (это количество классов, которые вы хотите тренировать)
3. При смене якорей на обучение результаты получают кластеризацией.
Например: в этой статье 3 класса, поэтому фильтры = 21, классы = 2.
Третий шаг, скопируйте файл веса, файл категории и файл cfg в папку bin программы C#.
Создайте новую программу WinForm, создайте новую папку yolov3 в папке bin/Debug, а затем поместите в нее файл веса, файл категории и файл cfg.
Четвертый шаг, скачать CSharpOpenCv
ссылка для скачивания:GitHub.com/is-туалет/open…
Я использую версию 4.1.0.
После распаковки скопируйте OpenCvSharp.Blob.dll, OpenCvSharp.dll, OpenCvSharp.Extensions.dll, OpenCvSharpExtern.dll в файл отладки, а затем обратитесь к проекту.
Четвертый шаг, напишите код C#
первый взгляд на страницу
Код:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using OpenCvSharp;
using OpenCvSharp.Dnn;
using OpenCvSharp.Extensions;
namespace yolov4_detect
{
public partial class Form1 : Form
{
static string Cfg = "../../bin/Debug/yolov3/yolov3.cfg";
static string Weight = "../../bin/Debug/yolov3/yolov3.weights";
static string Names = "../../bin/Debug/yolov3/coco_classes.txt";
string[] Labels = File.ReadAllLines(Names).ToArray();
Scalar[] Colors = Enumerable.Repeat(false, 2).Select(x => Scalar.RandomColor()).ToArray();
Net net;
public Form1()
{
InitializeComponent();
net = CvDnn.ReadNetFromDarknet(Cfg, Weight);
}
private Mat imagein;
private Mat imageout;
private void Form1_Load(object sender, EventArgs e)
{
imagein = new Mat();
imageout = new Mat();
imagein = Cv2.ImRead(@"yolov3/aircraft_8.jpg");
pictureBox2.Image = imagein.ToBitmap();
}
private void button1_Click(object sender, EventArgs e)
{
imageout = yolov3_model(imagein);
pictureBox1.Image = imageout.ToBitmap();
}
private Mat yolov3_model(Mat imgSrc)
{
Mat org = new Mat();
org = imgSrc;
const float threshold = 0.5f; //for confidence
const float nmsThreshold = 0.3f; //threshold for nms
var blob = CvDnn.BlobFromImage(org, 1.0 / 255, new OpenCvSharp.Size(416, 416), new Scalar(), true, false);
net.SetInput(blob);
var outNames = net.GetUnconnectedOutLayersNames();
var outs = outNames.Select(_ => new Mat()).ToArray();
net.Forward(outs, outNames);
org = GetResult(outs, org, threshold, nmsThreshold);
return org;
}
private Mat GetResult(IEnumerable<Mat> output, Mat image, float threshold, float nmsThreshold, bool nms = true)
{
//for nms
var classIds = new List<int>();
var confidences = new List<float>();
var probabilities = new List<float>();
var boxes = new List<Rect2d>();
var w = image.Width;
var h = image.Height;
/*
YOLO3 COCO trainval output
0 1 : center 2 3 : w/h
4 : confidence 5 ~ 84 : class probability
*/
const int prefix = 5; //skip 0~4
foreach (var prob in output)
{
for (var i = 0; i < prob.Rows; i++)
{
var confidence = prob.At<float>(i, 4);
if (confidence > threshold)
{
//get classes probability
double maxVal, minVal;
OpenCvSharp.Point min, max;
Cv2.MinMaxLoc(prob.Row[i].ColRange(prefix, prob.Cols), out minVal, out maxVal, out min, out max);
var classes = max.X;
var probability = prob.At<float>(i, classes + prefix);
if (probability > threshold) //more accuracy, you can cancel it
{
//get center and width/height
var centerX = prob.At<float>(i, 0) * w;
var centerY = prob.At<float>(i, 1) * h;
var width = prob.At<float>(i, 2) * w;
var height = prob.At<float>(i, 3) * h;
if (!nms)
{
// draw result (if don't use NMSBoxes)
Draw(image, classes, confidence, probability, centerX, centerY, width, height);
continue;
}
//put data to list for NMSBoxes
classIds.Add(classes);
confidences.Add(confidence);
probabilities.Add(probability);
boxes.Add(new Rect2d(centerX, centerY, width, height));
}
}
}
}
if (!nms) return null;
//using non-maximum suppression to reduce overlapping low confidence box
int[] indices;
CvDnn.NMSBoxes(boxes, confidences, threshold, nmsThreshold, out indices);
foreach (var i in indices)
{
var box = boxes[i];
Draw(image, classIds[i], confidences[i], probabilities[i], box.X, box.Y, box.Width, box.Height);
}
return image;
}
private void Draw(Mat image, int classes, float confidence, float probability, double centerX, double centerY, double width, double height)
{
//var label = $"{Labels[classes]} {probability * 100:0.00}%";
var label = Labels[classes] + "_" + probability.ToString("f2");
var x1 = (centerX - width / 2) < 0 ? 0 : centerX - width / 2; //avoid left side over edge
//draw result
image.Rectangle(new OpenCvSharp.Point(x1, centerY - height / 2), new OpenCvSharp.Point(centerX + width / 2, centerY + height / 2), Colors[classes], 2);
int baseline = 0;
var textSize = Cv2.GetTextSize(label, HersheyFonts.HersheyTriplex, 0.5, 1, out baseline);
Cv2.Rectangle(image, new OpenCvSharp.Rect(new OpenCvSharp.Point(x1, centerY - height / 2 - textSize.Height - baseline),
new OpenCvSharp.Size(textSize.Width, textSize.Height + baseline)), Colors[classes], Cv2.FILLED);
var textColor = Cv2.Mean(Colors[classes]).Val0 < 70 ? Scalar.White : Scalar.Black;
Cv2.PutText(image, label, new OpenCvSharp.Point(x1, centerY - height / 2 - baseline), HersheyFonts.HersheyTriplex, 0.5, textColor);
}
}
}
результат операции:
Загрузка кода: