4.32 How to create CustomYoloObjectDetector class based on C APIs of darknet-master?













To create Detector3 C++ class, you need the following changes:

  • 1. Change the struct name list in list,h to ylist to avoid ambiguity between this list and std::list.

  • 2. Change the struct name data in darknet,h to ydata to avoid ambiguity between this data and std::data.

  • 3. Create src_ex folder from the original src folder under darknet-master folder to use new list.h and data.h

  • 4. Create darknet_ext.h header file in src_ex folder from the original darknet.h in darknet-master/include folder to use new list.h and data.h.

  • 5. Create new solution yolo_cpp_dll_no_gpu_ext.sln and yolo_cpp_dll_no_gpu_ext.vcxproj and in darknet-master/build/darknet from the original yolo_cpp_dll_no_gpu.sln and yolo_cpp_dll_no_gpu.vcxproj to build the source files in darnet/src_ex.

  • 6. Build yolo_cpp_dll_ext.dll and yolo_cpp_dll_ext.lib from the new solution yolo_cpp_dll_no_gpu_ext.sln.


  • Note: You have to add export/import declaration in some header files in src_ex to be used in Detector3 class.

    The following is a source code of Detector3.h, which is a inline-method-based C++ class.
    /******************************************************************************
     *
     * Copyright (c) 2019 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
     *  Detector3.h
     *
     *****************************************************************************/
    
    #pragma once
    #define USE_STD
    
    #define IMPORT
    #define OPENCV
    
    #include <vector>
    #include <sol/opencv/OpenCVObject.h>
    #include <darknet_ext.h>
    #include <list.h>
    
    #include <box.h> 
    #include <data.h> 
    #include <image.h>
    #include <image_opencv.h> 
    #include <region_layer.h>
    #include <cost_layer.h>
    #include <utils.h>
    
    #include <network.h> 
    #include <option_list.h>
    #include <parser.h> 
    
    #include <fstream>
    #include <iostream>
    
    namespace SOL {
    
    class Detector3 {
    
    private:
      float thresh        = 0.24;
      float hier_thresh   = 0.5;
      ylist*    options   = nullptr;
      
      char*     name_list = nullptr;
      char**    names     = nullptr;
      std::vector<std::string> names_vector;
      
      image**   alphabet  = nullptr;
      network   net; 
    
    public:
      //Convert method from image to cv::Mat. 
      cv::Mat image_to_mat(image img)
      {
        int channels = img.c;
        int width = img.w;
        int height = img.h;
        cv::Mat mat = cv::Mat(height, width, CV_8UC(channels));
        int step = mat.step;
    
        for (int y = 0; y < img.h; ++y) {
          for (int x = 0; x < img.w; ++x) {
            for (int c = 0; c < img.c; ++c) {
              float val = img.data[c*img.h*img.w + y*img.w + x];
              mat.data[y*step + x*img.c + c] = (unsigned char)(val * 255);
            }
          }
        }
        return mat;
      }
    
    
      std::vector<std::string> read_object_names(const std::string&  filename, int& names_size) 
      {
        std::ifstream file(filename);
        std::vector<std::string> names;
        if (!file.is_open()) {
          return names;
        } 
        char line[256]; 
        int size = 0;
        while (file.getline(line, sizeof(line)-1)) {
          //printf("%d %s\n", size++, line);
          names.push_back(std::string(line) );
          size++;
        }
        names_size = size;
        std::cout << "object names loaded \n";
        return names;
      }
    
    
      void **list_to_array(std::vector<std::string>& list)
      {
        void** array = (void**)calloc(list.size(), sizeof(void*));
        int count = 0;
        for (int i = 0; i< list.size(); i++) {
          
          array[i] = (char*)list[i].c_str();
          printf("%s\n", array[i]);
        }
        return array;
      }
    
    
      image **load_alphabet_image(const char* path="C:/darknet-master")
      {
        const int nsize = 8;
        image** alphabets = (image**)calloc(nsize, sizeof(image*));
        printf("load_alphabet_image\n");
        for(int j = 0; j < nsize; ++j){
            alphabets[j] = (image*)calloc(128+1, sizeof(image));
            for(int i = 32; i < 127; ++i){
              char buff[256];
              sprintf(buff, "%s/data/labels/%d_%d.png", path, i, j);
              //printf("fullath:%s\n", buff);
              alphabets[j][i] = load_image_color(buff, 0, 0);
            }
        }
        return alphabets;
      }
    
      
    public:
      //
      // Constructor
      Detector3(const std::string darknet_root,  //"C:/darknet-master"
                const std::string& cfgfile, 
                const std::string& weightfile, 
                const std::string& cocofile,
                float thresh=0.24,
                float hier_thresh=0.5)
      :thresh(thresh),
      hier_thresh(hier_thresh)
      {
        options   = read_data_cfg((char*)cfgfile.c_str());
    
        int names_size = 0;
        names_vector   = read_object_names(cocofile, names_size); //coco_names; 
        names          = (char**)list_to_array(names_vector); 
    
        alphabet = load_alphabet_image((char*)darknet_root.c_str());
        
        net = parse_network_cfg_custom((char*)cfgfile.c_str(), 1, 1); // set batch=1
    
        load_weights(&net, (char*)weightfile.c_str());
    
        fuse_conv_batchnorm(net);
        calculate_binary_weights(net);
        if (net.layers[net.n - 1].classes != names_size) {
          printf(" Error: in the file %s number of names %d that isn't equal to classes=%d in the file %s \n",
              name_list, names_size, net.layers[net.n - 1].classes, cfgfile);
          if (net.layers[net.n - 1].classes > names_size) {
            getchar();
          }
        }
        srand(2222222);
        
      }
    
      //
      // Destructor
      ~Detector3()
      {
        // free memory
        free_ptrs((void**)names, net.layers[net.n - 1].classes);
        free_list_contents_kvp(options);
        free_list(options);
    
        const int nsize = 8;
        for (int j = 0; j < nsize; ++j) {
            for (int i = 32; i < 127; ++i) {
                ::free_image(alphabet[j][i]);
            }
            free(alphabet[j]);
        }
        free(alphabet);
    
        free_network(net);
      }
    
      //See detector.c file in C:/darknet-master/src
      
      //Return an image that bounding rectangles of detected objects and labels are drawn in an image corresponding to a filename.  
      //A csv ile of csv_filename is created, which includes detailed information on the detected objects.
      image detect_image(const char* filename, const char* csv_filename)
      {
        int j;
        float nms = .45;    // 0.4F
    
        image im      = load_image((char*)filename, 0, 0, net.c);
        
        image sized   = resize_image(im, net.w, net.h);
        int letterbox = 0;
        //image sized = letterbox_image(im, net.w, net.h); letterbox = 1;
        layer l = net.layers[net.n - 1];
    
        float *X = sized.data;
    
        //time= what_time_is_it_now();
        //double time = get_time_point();
        network_predict(net, X);
     
        //printf("%s: Predicted in %lf milli-seconds.\n", filename, ((double)get_time_point() - time) / 1000);
    
        int nboxes = 0;
        detection *dets = get_network_boxes(&net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes, letterbox);
    
        if (nms) {
          do_nms_sort(dets, nboxes, l.classes, nms);
        }
        int show_id_only = 1;
        draw_detections_v3_ext(im, dets, nboxes, thresh, names, alphabet, l.classes, csv_filename, show_id_only);
        
        save_image(im, "predictions");
        
        free_detections(dets, nboxes);
    
        free_image(sized);
        return im;
      }
    };
    
    }
    
    

    The following is a source code of CustomYoloObjectDetector.cpp.
    /*
     * CustomYoloObjectDetector.cpp 
     * Copyright (c) 2019 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
     */
    
    
    //2019/05/27 
      
    #define _CONSOLE_
    #define USE_STD
    
    #define  OPENCV
    
    #include <sol/yolo3/Detector3.h>
    
    #include <sol/ModuleFileName.h>
    #include <sol/DropFiles.h>
    
    #include <sol/ListView.h>
    #include <sol/StringList.h>
    
    #include <sol/Profile.h>
    #include <sol/StringT.h>
    #include <sol/opencv/OpenCVObject.h>
    #include <sol/opencv/OpenCVApplicationView.h>
    #include <sol/opencv/OpenCVNamedWindow.h>
    #include <sol/PushButton.h>
    #include <sol/FileDialog.h>
    
    #include "Resource.h"
    
    namespace SOL {
    
    class MainView :public OpenCVApplicationView {
    
    private:
      ////////////////////////////////////////////////////////////////////////////////////////
      //Inner class starts.
      class SimpleView :public OpenCVNamedWindow {
      private:
        StringT<char>  filePath;
        int            loadFlag;
        cv::Mat        image;
        
        void display()
        {
          show(image);
        }
        
      public:
        SimpleView(View* parent, const char* name, Args& args)
        :OpenCVNamedWindow(parent, name, args)
        {
          try {
          const char* filename = (const char*)args.get(XmNimageFileName);
          int imageLoadingFlag = (int)args.get(XmNimageLoadingFlag);
          
          loadImage(filename, imageLoadingFlag); //"..\\images\\WafukuMannequin.png");
          } catch (Exception& ex) {
            caught(ex);
          }
        }
        
        ~SimpleView()
        {
        }
         
        void loadImage(const char* filename, int flag=CV_LOAD_IMAGE_COLOR)
        {
          try {
            filePath = filename;
            loadFlag = flag;
            image    = readImage(filename, flag);
            refresh();
          } catch (Exception& ex) {
            caught(ex);
          }
        }
        
        void reload()
        {
          try {
            image    = readImage((const char*)filePath, loadFlag);
            refresh();
          } catch (Exception& ex) {
            caught(ex);
          }
        }
    
        void setImage(cv::Mat mat)
        {
          this->image = mat;
          refresh();
        }
      
        void writeImage(const char* filename)
        {
          OpenCVNamedWindow::writeImage(filename, image);
        }
      };
      //Inner class endss
      ////////////////////////////////////////////////////////////////////////////////////////
    
      StringT<char>           imageFile;
      StringT<char>           savedImageFile;
      SmartPtr<Detector3>      detector;
      
      SmartPtr<SimpleView>    view;
      SmartPtr<ListView>      listv;
    
      SmartPtr<PushButton>    reloadButton;
      SmartPtr<PushButton>    detectButton;
      String                  selectedFolder;
      std::string             darknet_root;
      std::string             cfg_filename;
      std::string             coco_filename;
      std::string             weight_filename;
      
      std::vector<std::string>       class_names;
      FileDialog              filedlg;
      Profile                 profile;
    
     void updateCaption()
      {
        char caption[MAX_PATH];
        sprintf_s(caption, CountOf(caption), "%s - %s", 
              (const char*)imageFile, 
              getAppName()); 
        setText(caption);
      }
      
      void resize(int w, int h)
      {
        if (view && listv && detectButton  && reloadButton) {
          view            -> reshape(         0,  40,  w-310, h-45);
          reloadButton    -> reshape(10,       4,     80,   28);
          detectButton    -> reshape(100 + 10, 4,     80,   28);
          listv           -> reshape(w-305,  40, 300, h-45);
        }
      }
    
      void open(Action& action)
      {
        Args ar;
        
        char dir[MAX_PATH] = {0};
        //Restore previously select folder from a registry(profile of this application) for fileDialog
        if (profile.getFileFolder(dir, CountOf(dir))) {
          ar.set(XmNdirectory, dir);
          filedlg.setValues(ar);
        }
        
        try {    
          if(filedlg.open()) {
            const char* filename = filedlg.getFileName();
            saveFileFolder(filename);
    
            openFile(filename);
            
          }
        } catch (Exception& ex) {
          caught(ex);
        }
      }
    
      
      void reload(Action& action)
      {
        if (view) {
          view ->reload(); 
        }
      }
      
    
      void detect(Action& action)
      {
        std::string filename((const char*)imageFile);
        printf("filename %s\n", (const char*)imageFile);
        const char* name = (const char*)imageFile;
        const char* bslash = strrchr((const char*)imageFile,'\\');
        if (bslash) {
          name = ++bslash;
        }
        std::string csv_filename = std::string(name) + ".csv";
        printf("csv_filename %s\n", csv_filename.c_str());
        
        image image = detector->detect_image(filename.c_str(), csv_filename.c_str());
        cv::Mat mat = detector->image_to_mat(image);
        cv::Mat mbgr;
        cv::cvtColor(mat, mbgr, cv::COLOR_RGB2BGR);
        free_image(image);
        view->setImage(mbgr);
        
        listv->readCSVFile(csv_filename.c_str(), True);
        
      }
    
    
      void openFile(const char* filename)
      {
        try {
          listv-> clear();
          
          view -> loadImage(filename, CV_LOAD_IMAGE_COLOR);
          imageFile = filename;
          /*const char* fname = strrchr(filename, '\\');
          if (fname) {
            fname++;
          }
          
          imageFile = fname;
          */
          updateCaption();
      
          savedImageFile = "";
          
        } catch (Exception& ex) {
          caught(ex);
        }
      }
      
      
      void dropFiles(Action& action)
      {
        char fileName[MAX_PATH] = { 0 };
        DropFiles drop((HDROP)action.getWParam());
    
        int num = drop.queryFile(0, fileName, CountOf(fileName));
        if(num > 0) {
          if (filedlg.isImageFileName(fileName)) {
            openFile(fileName);
            bringUp();
          } else {        
            bringUp(); //Activate and raise this view
            showErrorDialog("Invalid image filename", fileName,  MB_OK);
          }
        }    
      }
    
      void confirm(Action& action)
      {
        int rc = MessageBox(NULL, "Are you sure to close this window?", "Confirmation", 
                    MB_OKCANCEL|MB_ICONEXCLAMATION);
        if (rc == IDOK) {
          exit(action);
        }
      }
    
    
      const char** getListViewHeader(int& number)
      {
        static const char* LISTVIEW_HEADER[] = {"id ", "object ", "score", "  x ", "  y ", "  w ", "  h "};
        number = CountOf(LISTVIEW_HEADER);
        return LISTVIEW_HEADER;
      }
    
      
      void readIniFile()
      {
        // Read some YOLO configuration file names from the ini file ".\\YoloObjectDetector.ini".
        char buffer[256] = { 0 };
        
        const char* inifile = ".\\CustomYoloObjectDetector.ini";
        GetPrivateProfileString("DARKNET_ROOT","filename", "", buffer, sizeof(buffer), inifile);
        darknet_root    = buffer;
        printf("DARKNET_ROOT:%s\n", buffer);
    
        
        GetPrivateProfileString("CFG_FILE",    "filename", "", buffer, sizeof(buffer), inifile);
        cfg_filename    = buffer;
        printf("CFG_FILE:%s\n", buffer);
        
        GetPrivateProfileString("WEIGHT_FILE", "filename", "", buffer, sizeof(buffer), inifile);
        weight_filename = buffer;
        printf("WEIGHT_FILE:%s\n", buffer);
        
        GetPrivateProfileString("COCO_FILE",   "filename", "", buffer, sizeof(buffer), inifile);
        coco_filename   = buffer;
        printf("COCO_FILE:%s\n", buffer);
        
      }
    
    
    public:
      MainView(OpenCVApplication& applet, const char* name, Args& args)
      :OpenCVApplicationView(applet, name, args)
      {
        
        try {
          //Default image file name
          imageFile = "..\\..\\images\\PoliceCar.jpg";
    
          readIniFile();
    
          detector = new Detector3( darknet_root, 
                                    cfg_filename, 
                                    weight_filename,
                                    coco_filename);
          Args ar;
          ar.reset();
          ar.set(XmNimageFileName,  imageFile);
          ar.set(XmNimageLoadingFlag, CV_LOAD_IMAGE_COLOR);
          view = new SimpleView(this, "cvwindow", ar); 
          view -> addCallback(XmNdropCallback, this,
            (Callback)&MainView::dropFiles, NULL);
          
          ar.reset();
          ar.set(XmNexStyle, (LONG_PTR)WS_EX_CLIENTEDGE);
          ar.set(XmNstyle, (LONG_PTR)LVS_REPORT);
    
          listv = new ListView(this, "objects", ar);
    
          StringList header;
          int number = 0;
          const char** strings = getListViewHeader(number);
          for (int i = 0; i<number; i++) {
            header.add(strings[i]); 
          }
          
          listv->clear();
          listv->setColumn(&header);
           
          ar.reset();
          reloadButton = new PushButton(this, "Reload", ar);
          reloadButton -> addCallback(XmNactivateCallback, this, 
              (Callback)&MainView::reload, NULL); 
    
          ar.reset();
          detectButton = new PushButton(this, "Detect", ar);
          detectButton -> addCallback(XmNactivateCallback, this, 
              (Callback)&MainView::detect, NULL); 
        
          ar.reset();
          ar.set(XmNfilter, FileDialog::getImageFilesFilter());
          filedlg.create(this, "OpenFile", ar);
        
          addCallback(XmNmenuCallback, IDM_OPEN, this,
              (Callback)&MainView::open, NULL);
          addCallback(XmNmenuCallback, IDM_EXIT, this,
              (Callback)&MainView::confirm, NULL);
    
          updateCaption();
        } catch (Exception& ex) {
          caught(ex);
        }
      }
    
      ~MainView()
      {
      }
      
    
      void save(Action& action)
      {
        try {
          if (!savedImageFile.isEmpty()) {
            //Write detected image into the filename.
            view->writeImage(savedImageFile);
          } else {
            saveAs(action);
          }
        } catch (Exception& ex) {
          caught(ex);
        }
      }
      
      //2017/12/01
      void saveAs(Action& action)
      {
        Args ar;
        
        char dir[MAX_PATH] = { 0 };
        if (restoreFileFolder(dir, CountOf(dir))) {
          ar.set(XmNdirectory, dir);
          filedlg.setValues(ar);
        }
        
        try {    
          if(filedlg.save()) {
        
            const char* filename = filedlg.getFileName();
            saveFileFolder(filename);
            
            //Write detected image into the filename.
            view->writeImage(filename);
            savedImageFile = filename;
          }
        } catch (Exception& ex) {
          caught(ex);
        }
      }  
    };
    }
    
    //
    void main(int argc, char** argv) 
    {
      try {
        ModuleFileName module(argv[0]);
        
        const char*  name = module.getAppName();
        OpenCVApplication applet(name, argc, argv);
    
        Args args;
        args.set(XmNwidth,  1000);
        args.set(XmNheight, 500);
        MainView view(applet, name, args);
        view.realize();
    
        applet.run();
        
      } catch (SOL::Exception& ex) {
        caught(ex);
      }
    }
    
    

    Last modified: 27 May 2019

    Copyright (c) 2019 Antillia.com ALL RIGHTS RESERVED.