/******************************************************************************
*
* Copyright (c) 2019 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer.
*
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Detector3.h
*
*****************************************************************************/
// 2019/06/07
// This class is mainly based on C APIs of detector.c and image.c in darknet-master/src folder.
// Note:
// 1. Changed the class name list in list,h to ylist to avoid ambiguity between this list and std::list.
// 2. Changed the class name data in darknet,h to ydatat to avoid ambiguity between this data and std::data.
// 3. Chanaged list and data to ylist and ydata in files in darknet-master/src, if defined USE_STD maro.
// 4. Removed fvisibility=hidden of Makefiel in darknet-master folder.
//
// 5. Added D_GLIBCXX_USE_CXX11_ABI macro to compilineg option in the following way.
//
// $(CPP) -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=0 $(COMMON) $(CFLAGS) -c $< -o $@
#pragma once
#define USE_STD
#define IMPORT
#define OPENCV
#include <vector>
#include <oz++/opencv/OpenCVObject.h>
#include <darknet.h>
#include <list.h>
#include <box.h> //do_nms_sort
#include <data.h> //get_labels_custom
#include <image.h> //load_alphabet, load_image
#include <image_opencv.h> //load_image
#include <region_layer.h>
#include <cost_layer.h>
#include <utils.h>
#include <network.h> //fuse_conv_batchnorm, calculate_binary_weights, network_predict, get_network_boxes
#include <option_list.h> //read_data_cfg
#include <parser.h> //load_weight, parse_network_cfg_custom
#include <fstream>
#include <iostream>
namespace OZ {
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++) {
//Shallow copy
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((void**)names); //Modified to Detector3 class.
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 /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;
}
};
}