yoloV 3 darknet GPUハンドルコンパイルからトレーニングまでC++呼び出しAPI


まず、システムはUbuntu 18であることを宣言します.04、私の機械はopencv 4を取り付けました.1.1とcuda 10.0の、プロセスは私の別のブログを参考にすることができて、この部分はIDEがqtcreatorを使うことを説明しません
1.Darknetのダウンロード
Githubリンク公式サイトリンク
これは言うまでもなく、直接git clone、ネットがよくなくても構わない.
git clone https://github.com/pjreddie/darknet

予備トレーニングウェイトdarknet 53もダウンロードする.conv.74これは先に置いて、後でそれを訓練フォルダにしてダウンロードして完成した後にdarknetフォルダに入ります
2.Makefileの構成
コードの修正を容易にするため、VSCodeを使用してdarknetがあるフォルダを開きます.
Makefileファイルを見つけて開き、最初の5行は構成のパラメータで、OPENCV、GPU、CUDNNはすべて開きます
GPU=0
CUDNN=0
OPENCV=0
OPENMP=0
DEBUG=0

修正が済んだらこうなります
GPU=1
CUDNN=1
OPENCV=1
OPENMP=0
DEBUG=0

後のグラフィックカードの計算能力の構成についてこの参考の別のブログ、私は改正しないで問題にぶつかることがありません
ARCH= -gencode arch=compute_30,code=sm_30 \
      -gencode arch=compute_35,code=sm_35 \
      -gencode arch=compute_50,code=[sm_50,compute_50] \
      -gencode arch=compute_52,code=[sm_52,compute_52]
#      -gencode arch=compute_20,code=[sm_20,sm_21] \ This one is deprecated?

# This is what I use, uncomment if you know your arch and want to specify
# ARCH= -gencode arch=compute_52,code=compute_52

42行目を見つけてpkg-congifのパッケージ名を変更し、opencv 4の名前はopencv 4で、両方を変更します.
ifeq ($(OPENCV), 1) 
COMMON+= -DOPENCV
CFLAGS+= -DOPENCV
LDFLAGS+= `pkg-config --libs opencv` -lstdc++
COMMON+= `pkg-config --cflags opencv` 
endif

これで、他は動かなくてもいいです.
ifeq ($(OPENCV), 1) 
COMMON+= -DOPENCV
CFLAGS+= -DOPENCV
LDFLAGS+= `pkg-config --libs opencv4` -lstdc++
COMMON+= `pkg-config --cflags opencv4` 
endif

次はcudaのライブラリで、自分のパスと一致しているかどうかをチェックして、私のデフォルトでいいです.
ifeq ($(GPU), 1) 
COMMON+= -DGPU -I/usr/local/cuda/include/
CFLAGS+= -DGPU
LDFLAGS+= -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand
endif

このMakefileの構成が完了しました
3.コンパイルし、いくつかのエラーを修正する
ショートカットキーCtrl+shift+Pを押してVSCodeのコマンドを開き、C/C++を検索し、編集構成(UI)を開き、UIを使わずに勝手に
  • 含むパスを見つけて、opencvの含むパスとcudaの含むパスを追加して、後でコードを修正して簡単にopencvの含むアドレスを見るためにpkg-configでいいです.例えば、私の書いた/usr/local/include/opencv4/**は自分で探せばいいです.cudaの見つからないようにして、一般的に/usr/local/cuda/includeの中で、私の追加は以下の
  • のようにします.
    ${workspaceFolder}/**
    /usr/local/cuda/include/
    /usr/local/include/opencv4/**
    
  • グローバルを追加して、下の格の中で、配置パラメータは開いて何を記入して、私のはこのように
  • です
    OPENCV
    CUDNN
    GPU
    

  • do{
        make;
        if(    ){
        		  ;
        		continue;
        }
        else break;
    }while(1); 	//      
    

    実は簡単にCtrl+shift+`を押してVSCodeの端末を開けて、makeを実行してそれから間違いを報告します
    ./src/image_opencv.cpp:12:1: error: ‘IplImage’ does not name a type; did you mean ‘image’?
     IplImage *image_to_ipl(image im)
     ^~~~~~~~
    

    これは彼がopencv 3の構造と命名を書いたためで、4のいくつかの変動はCtrlを押して、マウスは./src/image_opencv.cpp:12:1直接この位置に移動します.これは私がVSCodeを使った大きな理由で一番上にヘッダファイルを追加しました.
    #include "opencv2/imgproc/imgproc_c.h"
    

    この間違いは解決しましたそして
    ./src/image_opencv.cpp:76:20: error: ‘CV_CAP_PROP_FRAME_WIDTH’ was not declared in this scope
        if(w) cap->set(CV_CAP_PROP_FRAME_WIDTH, w);
    

    これがネーミングの変化で、このような前のCVを削除すればいい、この間違いはimageにある.opencv.cppの中で、このファイルは後で役に立ちます.覚えておいてください.間違いを直すとコンパイルが完了します.
    4.独自のデータセットを作成する
    まずフォルダを建てて、名前は任意で、中国語を持たないでください、あなたは知っていてそれから以下はすべてこの中でdarknet YoloV 3のデータセットのフォーマットを操作するのはテキストのドキュメントで、構造は簡単で1つのJPEGImagesフォルダで、中に画像を置いて、すべてJPGフォーマットで、そうでなければopencvで自分でプログラムを書いて自動的に別のlabelsフォルダを変換することができますタグファイルを保存するのもtxt形式です文書ドキュメントタグファイルの具体的な形式は、ファイル名と対応するJPGファイル名が同じであることを保証します
    2 0.341624 0.582733 0.00729167 0.0175926
    1 0.433048 0.624979 0.0286458 0.062963
    4 0.265383 0.666638 0.0776042 0.125926
    #ID    box   X      box   Y          W        H
    #        0-1,      
    #               
    

    それから2つのフォルダは必ず一緒に置いて、darknetは直接自動的に画像ファイルの../labels/xxxxxx.txtに対してマークファイルの再構築backupフォルダを探して、訓練の重みファイルのバックアップweigitsフォルダを保存して、訓練が完了して保留する重みファイルを保存するために、この自発的なImageSetsフォルダ、画像セットを保存するために2つのテキストドキュメントを再構築します.画像セットにはtrainという名前があります.txtは訓練のためのピクチャセットで、中にJPGピクチャの経路を保存して、絶対経路を推薦して、1行の1つの経路のもう1つのval.txtはフォーマットが同じImageSetsフォルダを検証して、新しいvocを作ります.names文書ドキュメントには、ターゲットのラベルが書かれています.たとえば、
    car
    people
    bicycle
    bird
    boat
    

    1行1つ私が5つの目標を例にvocを新規作成しました.data文書ドキュメントには5行あります
    classes=       
    train  = /...../        /ImageSets/train.txt
    valid  = /...../        /ImageSets/val.txt
    names  = /...../        /voc.names
    backup = /...../        /backup
    

    もう1つの最も重要なcfgファイルは、cfg/yolov3-voc.cfgから1つコピーして、あなたのフォルダに行って、名前を変更することができて、接尾辞は保留して(実は大丈夫です)、それから前のいくつかの行を修正します[net]の下
    [net]
    # Testing
     batch=1						#batch            ,       ,         
     subdivisions=1 		#subdivisions          batch   
     										#           batch/subdivisions,                
    # Training
    # batch=64
    # subdivisions=16
    width=416					#width height  channels        ,      ,            
    height=416
    channels=3
    momentum=0.9
    decay=0.0005
    angle=0
    saturation = 1.5
    exposure = 1.5
    hue=.1
    
    learning_rate=0.001		#   ,    
    burn_in=1000
    max_batches = 50200	#      
    policy=steps						#             ,         
    steps=40000,45000			#    40000      0.1,45000    0.1
    scales=.1,.1						#            ,          
    

    次に[yolo]の項を探して、全部で3つあって、608行で、692行で、776行は1つを例にとって、その他の3つは同じです
    [convolutional]
    size=1
    stride=1
    pad=1
    filters=10							#      ,       ,  3*(    +5),            
    activation=linear
    
    [yolo]
    mask = 0,1,2
    anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
    classes=20						#    
    num=9
    jitter=.3
    ignore_thresh = .5
    truth_thresh = 1
    random=1						#     ,      ,    ,      
    											#                ,    
    

    最初にダウンロードしたプリトレーニングの重みを加えると、最終的なフォルダ構造が完了します.
            
    	╞backup
    	╞ImageSets
    	║	└train.txt  val.txt
    	╞JPEGImages
    	║	└    
    	╟labels
    	║	└    
    	╟weigits
    	╟voc.names  
    	╟voc.data 
    	╟yolov3-voc.cfg 
    	└darknet53.conv.74
    

    トレーニングセットのラベルや写真集の生成については、みんなコードを使っていると思いますが、サボる方法が思いつかないわけがないでしょう.
    4.1オートメーションツールを少しください
    もともと直接訓練を始めることができて、しかし私は比較的に怠け者で、命令が長すぎるのが嫌で、それからこれがありました
    DARKNET_PATH=  darknet  
    
    LOCAT=${PWD}		#                         
    
    classes=5
    TRAIN_PATH=${LOCAT}/ImageSets/train.txt
    VALID_PATH=${LOCAT}/ImageSets/val.txt
    NAMES_PATH=${LOCAT}/voc.names
    BACKUP_PATH=${LOCAT}/backup
    
    DATA_PATH=${LOCAT}/voc.data
    CFG_PATH=${LOCAT}/yolov3-voc.cfg
    MODEL_PATH=${LOCAT}/darknet53.conv.74
    
    cat>${DATA_PATH}<<EOF
    classes= ${classes}
    train  = ${TRAIN_PATH}
    valid  = ${VALID_PATH}
    names  = ${NAMES_PATH}
    backup = ${BACKUP_PATH}
    EOF
    
    cd ${DARKNET_PATH}
    
    if [ "$1" = "train" ];then
        if [ "$2" = "" ];then
            ./darknet detector train ${DATA_PATH} ${CFG_PATH} darknet53.conv.74 -gpus 0
        else
            if [ "$2" = "goon" ];then
                WEIGHT=${BACKUP_PATH}/yolov3-voc.backup
                ./darknet detector train ${DATA_PATH} ${CFG_PATH} ${WEIGHT} -gpus 0
            else
                ./darknet detector train ${DATA_PATH} ${CFG_PATH} $2 -gpus 0
            fi
        fi
    fi
    if [ "$1" = "test" ];then
        WEIGHT=${BACKUP_PATH}/yolov3-voc.backup
        if [ "$3" = "" ];then
            ./darknet detector test ${DATA_PATH} ${CFG_PATH} ${WEIGHT} -gpus 0 $2
        else
            ./darknet detector test ${DATA_PATH} ${CFG_PATH} $3 $2 -gpus 0
        fi
    fi
    
    

    前は簡単ですが、使用方法は、./xxxx.sh trainがトレーニング(デフォルトではdarknet 53.conv.74トレーニング)./xxxx.sh train goonが前回のウェイトトレーニングを継続することです.(デフォルトではbackup/yolov 3-voc.backupトレーニングを使用)./xxxx.sh train は別の重みファイルに基づいて./xxxx.sh test JPG を訓練します.その画像テスト./xxxx.sh test JPG を使用します.その画像と重みファイルテストを使用して、このスクリプトをトレーニングセットフォルダに入れてください.
    5.トレーニング開始
    前の行の変数を設定すれば./xxxx.sh trainになります.プレビュー効果は./xxxx.sh test JPG になります.
    6.C++呼び出し
    まずdarknetディレクトリの下にinclude/darknet.hとlibdarknet.so/usr/localの下にフォルダを新規作成するなど、他の安全な場所にコピーし、その共有ライブラリをシステムに追加します.
    cd /etc/ld.so.conf.d/
    sudo vim darknet.conf  #                 
    sudo ldconfig					#       
    

    新しいqtcreatorのプロジェクト(qtcreatorを例に挙げると、操作は他のプラットフォームで再現できます)proファイルにopencvを追加します.(これは詳しくは言いません)darknet.hの参照ディレクトリを追加し、cudaの参照ディレクトリを追加します.前述したように、一般的に/usr/local/cuda/includeにdarknetとcudaのライブラリパスを追加し、OPENCVを追加するVSCodeに追加したいくつかのマクロを追加するのはこのようなことです.
    #darknet  cuda,    cuda       
    INCLUDEPATH += /usr/local/darknet /usr/local/cuda/include
    LIBS += -L/usr/local/darknet/lib -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand -lcudnn -ldarknet
    DEFINES += GPU OPENCV CUDNN     #darknet        ,       
    

    VSCodeに戻ってdarknetのソースコードを読んでexamples/darknetを見つけます.c,中はmain関数を含んで、沿って関数を探し当てることができて、呼び出しはきっとテストとあまり差がなくて、detectorコマンドを識別する場所を探し当てます
        } else if (0 == strcmp(argv[1], "detector")){
            run_detector(argc, argv);
    

    この関数を定義します
        if(0==strcmp(argv[2], "test")) test_detector(datacfg, cfg, weights, filename, thresh, hier_thresh, outfile, fullscreen);
        else if(0==strcmp(argv[2], "train")) train_detector(datacfg, cfg, weights, gpus, ngpus, clear);
    

    定義test_detectorまでtestコマンドとtrainコマンドが見つかります
    void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen)
    {
    /*
        printf("  test_detector
    " "datacfg = %s
    " // "cfgfile = %s
    " // "weightfile = %s
    " // "filename = %s
    " // "thresh = %f
    " // , 0.5 "hier_thresh = %f
    "//? = 0.5 "outfile = %s
    " // "fullscreen = %d
    " // , datacfg, cfgfile, weightfile, filename, thresh, hier_thresh, outfile, fullscreen);*/
    list *options = read_data_cfg(datacfg); // .data $ char *name_list = option_find_str(options, "names", "data/names.list"); // $ /*printf("name_list = %s
    ", name_list);*/
    char **names = get_labels(name_list); // $ image **alphabet = load_alphabet(); // network *net = load_network(cfgfile, weightfile, 0); // $ set_batch_network(net, 1); // (CUDNN )$ srand(2222222); double time; char buff[256]; char *input = buff; float nms=.45; while(1){ if(filename){ // , strncpy(input, filename, 256); } else { // printf("Enter Image Path: "); fflush(stdout); input = fgets(input, 256, stdin); if(!input) return; strtok(input, "
    "
    ); } image im = load_image_color(input,0,0); // (load_image_cv ) image sized = letterbox_image(im, net->w, net->h); // $ //image sized = resize_image(im, net->w, net->h); //image sized2 = resize_max(im, net->w); //image sized = crop_image(sized2, -((net->w - sized2.w)/2), -((net->h - sized2.h)/2), net->w, net->h); //resize_network(net, sized.w, sized.h); layer l = net->layers[net->n-1]; float *X = sized.data; // time=what_time_is_it_now(); // network_predict(net, X); // $ printf("%s: Predicted in %f seconds.
    "
    , input, what_time_is_it_now()-time); int nboxes = 0; // bandbox $ detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes); //printf("%d
    ", nboxes);
    //if (nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms); if (nms) do_nms_sort(dets, nboxes, l.classes, nms); draw_detections(im, dets, nboxes, thresh, names, alphabet, l.classes); // bandbox free_detections(dets, nboxes); // bandbox if(outfile){ save_image(im, outfile); // outfile , outfile } else{ save_image(im, "predictions"); #ifdef OPENCV make_window("predictions", 512, 512, 0); show_image(im, "predictions", 0); #endif } free_image(im); free_image(sized); if (filename) break; } }

    これが最良のルーチンです.$が終わるのは必須のステップです.
    Opencvで画像を読み取りたい場合は、src/image_opencv.cppのmat_を見つけます.to_イメージはopencvのMatをYoloに変換して受け入れるフォーマットが関数、ipl_to_imageとrgbgr_イメージはすぐそばにあるので、必要なものをコピーしてください.
    image mat_to_image(Mat m)
    {
    	IplImage ipl = m;
    	image im = ipl_to_image(&ipl);
    	rgbgr_image(im);
    	return im;
    }
    

    detection構造体の説明については、私も理解していませんが、これらは十分です.
    typedef struct
    {
    	//      bandbox  (    )
    	float x, y, w, h;
    } box;
    
    typedef struct detection
    {
    	boxbbox;           //bandbox
    	int classes;
    	float *prob;        	//  ,        
    	float *mask;
    	float objectness;
    	int sort_class;
    } detection;
    

    最後にルーチンを1部送る
    #include
    #include
    #include 
    #include
    
    using namespace std;
    using namespace cv;
    
    #define datacfg     "/home/yao/Documents/yoloV3/darknet/VOCdevkit/VOC2007/voc.data"
    #define cfgfile     "/home/yao/Documents/yoloV3/darknet/VOCdevkit/VOC2007/yolov3-voc.cfg"
    #define weightfile  "/home/yao/Documents/yoloV3/darknet/VOCdevkit/VOC2007/weigits/640_480_3K_5.23.weights"
    #define videosrc    "/home/yao/Videos/0.mp4"
    #define SavePAYH    "/home/yao/Videos/640_480_3K_5.23.avi"
    #define PicturePATH "/home/yao/Downloads/qq-files/1790931015/file_recv/BL}A~RL7L3Y0({BI0[}JGRT.png"
    #define SaveVideo 1
    #define USE_Picture 0
    
    void drawBox(Mat& Src, const Scalar& color, box Box, const char* name, float prob);
    Scalar get_Scalar(int index, int classes);
    image cvmat_to_image(cv::Mat m);
    
    int main()
    {
        list *options = read_data_cfg(datacfg);                          //      
        char *name_list = option_find_str(options, "names", NULL);          //           
        char **names = get_labels(name_list);                               //              
        int classes = option_find_int(options, "classes", 2);               //       
        network *net = load_network(cfgfile, weightfile, 0);             //              
        set_batch_network(net, 1);                                          //    (CUDNN  )
    
    #if (USE_Picture == 0)
        VideoCapture CAP(videosrc);
    //         
    //    CAP.open("/dev/video0", CAP_V4L2);
    //    CAP.set(CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
    //    CAP.set(CAP_PROP_FRAME_HEIGHT, 480);
    //    CAP.set(CAP_PROP_FRAME_WIDTH, 640);
    //    CAP.set(CAP_PROP_AUTO_WB, 1);
    //    CAP.set(CAP_PROP_AUTO_EXPOSURE, 0);
    //    CAP.set(CAP_PROP_EXPOSURE, 121);
    //    CAP.set(CAP_PROP_FPS, 120);
    #endif
    
    #if(SaveVideo == 1)
        VideoWriter Writer(SavePAYH, VideoWriter::fourcc('M', 'J', 'P', 'G'), 30, Size(1920, 1080));
        if(!Writer.isOpened())
        {
            cout << "Writer Error" << endl;
            exit(-1);
        }
    #endif
    
        Mat Src, Dst;
        int nboxes = 0;
        namedWindow("Video", WINDOW_NORMAL);
        resizeWindow("Video", 1280, 720);
    #if (USE_Picture == 0)
        double time1 = 0;
        double time2 = 0;
        char buff[100] = { 0 };
        if(CAP.isOpened())
        {
            while(CAP.read(Src))
            {
                time1 = (double)getCPUTickCount();
    #else
        Src = imread(PicturePATH);
    #endif
                resize(Src, Dst, Size(net->w,net->h));                                        //         
                image im = cvmat_to_image(Dst);                                             //      Yolo  
                network_predict(net, im.data);                                                  //          
                detection *dets = get_network_boxes(net, im.w, im.h, 0.5, 0.5, 0, 1, &nboxes);  //  bandbox  
                cout << nboxes << endl;
                for(int i = 0; i < nboxes; ++i)
                {
                    //cout << i << ": objectness=" << dets[i].objectness << endl;
                    float probmax = dets[i].prob[0];
                    int index = 0;
                    for(int j = 0; j < dets->classes; ++j)
                    {
                        if(probmax < dets[i].prob[j])       //            
                        {
                            probmax = dets[i].prob[j];
                            index = j;
                        }
                        //cout << "names=" << names[j] << " prob=" << dets[i].prob[j] << endl;
                    }
                    if(probmax < 0.5) //           
                    {
                        cout << "throw" << endl;
                        continue;
                    }
    
    
                    drawBox(Src, get_Scalar(index, classes), dets[i].bbox, names[index], probmax);
                }
    
    #if (USE_Picture == 0)
    #if(SaveVideo == 1)
                time2 = (double)getCPUTickCount();
                double usetime = (time2 - time1) / getTickFrequency();
    
                sprintf(buff, "Time = %4.3lfms  FPS = %2.2lf BoxConut = %d", usetime * 1000.0, 1.0 / usetime, nboxes);
                cout << buff << endl;
    
                putText(Src, buff, Point(25, 100), FONT_ITALIC, 1, Scalar(0, 255, 0), 2);
                putText(Src, weightfile, Point(25, 50), FONT_ITALIC, 1, Scalar(0, 255, 0), 2);
    
                imshow("Video", Src);
                Writer.write(Src);
    #endif
                waitKey(1);
    #else
                imshow("Video", Src);
                waitKey(100000);
    #endif
    
                free_image(im);                   //free!!!!!!! C   darknet        
                free_detections(dets, nboxes);    //free!!!!!!! C   darknet        
    #if (USE_Picture == 0)
            }
        }
        else
        {
            cout << "CAP Error" << endl;
        }
    #endif
        free_network(net);                  //free!!!!!!! C   darknet        
        free_ptrs((void**)names, classes);  //free!!!!!!! C   darknet        
        free_list(options);                 //free!!!!!!! C   darknet        
        cout << "Done!" << endl;
    }
    
    //  bandbox        
    void drawBox(Mat& Src, const Scalar& color, box Box, const char* name, float prob)
    {
        int left  = (Box.x-Box.w/2.)*Src.cols;
        int right = (Box.x+Box.w/2.)*Src.cols;
        int top   = (Box.y-Box.h/2.)*Src.rows;
        int bot   = (Box.y+Box.h/2.)*Src.rows;
    
        if(left < 0) left = 0;
        if(right > Src.cols-1) right = Src.cols-1;
        if(top < 0) top = 0;
        if(bot > Src.rows-1) bot = Src.rows-1;
    
        Point p1(left, top);
        Point p2(right, bot);
    
        putText(Src, name + to_string(prob * 100) + "%", p1 - Point(0, 3), FONT_HERSHEY_PLAIN, 1, color);
        rectangle(Src, p1, p2, color, 2);
    }
    
    float get_color(int c, int x, int max)
    {
        float colors[6][3] = { {1,0,1}, {0,0,1},{0,1,1},{0,1,0},{1,1,0},{1,0,0} };
        float ratio = ((float)x / max) * 5;
        int i = floor(ratio);
        int j = ceil(ratio);
        ratio -= i;
        float r = (1-ratio) * colors[i][c] + ratio*colors[j][c];
        return r;
    }
    
    Scalar get_Scalar(int index, int classes)
    {
        int offset = index * 123457 % classes;
        int red = get_color(0, offset, classes) * 255;
        int green = get_color(1, offset, classes) * 255;
        int blue = get_color(2, offset, classes) * 255;
        return Scalar(blue, green, red);
    }
    
    image cvipl_to_image(IplImage *src)
    {
        int h = src->height;
        int w = src->width;
        int c = src->nChannels;
        image im = make_image(w, h, c);
        unsigned char *data = (unsigned char *)src->imageData;
        int step = src->widthStep;
        int i, j, k;
    
        for (i = 0; i < h; ++i)
        {
            for (k = 0; k < c; ++k)
            {
                for (j = 0; j < w; ++j)
                {
                    im.data[k * w * h + i * w + j] = data[i * step + j * c + k] / 255.;
                }
            }
        }
        return im;
    }
    
    image cvmat_to_image(cv::Mat m)
    {
        IplImage ipl = m;
        image im = cvipl_to_image(&ipl);
        rgbgr_image(im);
        return im;
    }
    
    

    もうここまで来たんだから無駄なことを考えないで