第一步,将训练好的.h5文件转给.weight文件


     模型训练时采用TensorFlow + Keras框架,其参数模型输出为.h5文件,其中包含了yolo训练结果中的卷积层和池化层参数。
     OpenCV库,其中深度神经网络(dnn)模块采用了DarkNet框架,该框架导入的模型为.weights文件,并依赖于.cfg文件所定义的架构。所以,如果想将前面的框架训练的模型用于OpenCV中,就需要将yolo.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、filters = 3 * ( 5 + classes)
2、classes = n (这个是你要训练的类的数量)

3,anchors改为训练时,聚类得到的结果。
例如:本文这里有3个类,所以filters=21,classes=2。

第三步、将weight文件、类别文件和cfg文件复制到C#程序的bin文件夹里

 新建WinForm程序,在bin/Debug下新建yolov3文件夹,然后把weight文件、类别文件、cfg文件放进去

第四步、下载CSharpOpenCv

下载地址:https://github.com/shimat/opencvsharp/releases?after=4.5.1.20201226

我用的版本是4.1.0版本。

解压后,将OpenCvSharp.Blob.dll、OpenCvSharp.dll、OpenCvSharp.Extensions.dll、OpenCvSharpExtern.dll复制到Debug文件,然后引用到项目里面。

第五步、编写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);
        }
 
 
 
    }
}

运行结果:

代码下载:

https://download.csdn.net/download/hhhhhhhhhhwwwwwwwwww/13852328

————————————————
版权声明:本文为CSDN博主「AI浩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hhhhhhhhhhwwwwwwwwww/article/details/111772539