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.