24 Commits

Author SHA1 Message Date
Dimitrii Lopanov
0fd261e605 upd 2020-04-08 09:41:32 +03:00
Dimitrii
4c39be01f9 ptr causes segfault 2020-03-24 11:50:07 +03:00
Dimitrii
2dd85a84d6 rdm 2020-02-26 08:41:26 +03:00
Dimitrii
3a10d5a657 Merge branch 'master' of github.com:LdDl/go-darknet 2020-02-26 08:40:40 +03:00
Dimitrii
95332e0877 upd readmes 2020-02-26 08:34:03 +03:00
Dimitrii Lopanov
630ded761f Update README.md 2020-02-25 18:11:31 +03:00
Dimitrii Lopanov
674ff110d5 Update README.md 2020-02-25 08:26:00 +03:00
Dimitrii
f70cb0f1e6 upd readme 2020-02-21 15:52:39 +03:00
Dimitrii
49f6b85ba6 gitignore 2020-02-21 15:51:24 +03:00
Dimitrii
1f72563ea0 gitignore 2020-02-21 15:51:12 +03:00
Dimitrii
2c8e594cc7 vscode 2020-02-21 15:50:41 +03:00
Dimitrii
8e0371f19a upd readme 2020-02-21 15:50:10 +03:00
Dimitrii
380db18c1e upd gitignore 2020-02-21 15:48:02 +03:00
Dimitrii
feb1a0353d upd readme 2020-02-21 15:47:21 +03:00
Dimitrii
e486364a2b excess files 2020-02-20 18:10:34 +03:00
Dimitrii
bc4192f727 GOD SAVE STACKOVERFLOW 2020-02-20 18:10:11 +03:00
Dimitrii
48544ab339 got segfault 2020-02-20 17:02:08 +03:00
Dimitrii
fd2b030c94 ? 2020-02-20 13:17:29 +03:00
Dimitrii
f9e8a32a9e ? 2020-02-20 13:16:58 +03:00
Dimitrii
1fad0385f3 readme 2020-02-20 09:38:36 +03:00
Dimitrii
9d89162235 need to check what happens to im.data 2020-02-20 09:35:57 +03:00
Dimitrii
2b2f3f159c need to deal with image 2020-02-19 14:52:19 +03:00
Dimitrii
30fbbb9949 add network 2020-02-18 15:36:17 +03:00
Dimitrii
87175172a2 upd git ignore 2020-02-18 13:21:18 +03:00
22 changed files with 308 additions and 241 deletions

15
.gitignore vendored
View File

@@ -1,10 +1,13 @@
example/main
example/sample.jpg
example/coco.data
example/coco.names
example/yolov3-320.cfg
example/yolov3-320.weights
example/yolov3-416.cfg
example/yolov3-416.weights
example/yolov3.cfg
example/yolov3.weights
example/yolov3.weights
darknet.h
*.so
predictions.png
predictions.jpg
main
bad.list
data
.vscode

106
README.md
View File

@@ -1,19 +1,23 @@
# FORK of go-darknet https://github.com/gyonluks/go-darknet
# go-darknet: Go bindings for Darknet
[![GoDoc](https://godoc.org/github.com/LdDl/go-darknet?status.svg)](https://godoc.org/github.com/LdDl/go-darknet)
go-darknet is a Go package, which uses Cgo to enable Go applications to use
YOLO in [Darknet].
# go-darknet: Go bindings for Darknet
### go-darknet is a Go package, which uses Cgo to enable Go applications to use YOLO in [Darknet].
## License
#### Since this repository https://github.com/gyonluks/go-darknet is no longer maintained I decided to move on and make little different bindings for Darknet.
#### This bindings aren't for [official implementation](https://github.com/pjreddie/darknet) but for [AlexeyAB's fork](https://github.com/AlexeyAB/darknet).
go-darknet follows [Darknet]'s [license].
## Table of Contents
- [Requirements](#requirements)
- [Installation](#installation)
- [Usage](#usage)
- [Documentation](#documentation)
- [License](#license)
## Requirements
For proper codebase please use fork of [darknet](https://github.com/LdDl/darknet)
There are instructions for defining GPU/CPU + function for loading image from memory.
For proper codebase please use fork of [darknet](https://github.com/AlexeyAB/darknet). Latest commit I've tested [here](https://github.com/AlexeyAB/darknet/commit/a234a5022333c930de08f2470184ef4e0c68356e)
In order to use go-darknet, `libdarknet.so` should be available in one of
the following locations:
@@ -26,53 +30,85 @@ Also, [darknet.h] should be available in one of the following locations:
* /usr/include
* /usr/local/include
## Install
To achieve it, after Darknet compilation (via make) execute following command:
```shell
sudo cp libdarknet.so /usr/lib/libdarknet.so && sudo cp include/darknet.h /usr/local/include/darknet.h
```
Note: do not forget to set LIBSO=1 in Makefile before executing 'make':
```Makefile
LIBSO=1
```
## Installation
```shell
go get github.com/LdDl/go-darknet
```
The package name is `darknet`.
## Usage
## Use
Example Go program is provided in the [example] directory. Please refer to the code on how to use this Go package.
Example Go code/program is provided in the [example] directory. Please
refer to the code on how to use this Go package.
Building and running the example program is easy:
Building and running program:
Navigate to [example] folder
```shell
cd $GOPATH/github.com/LdDl/go-darknet/example
#download dataset (coco.names, coco.data, weights and configuration file)
```
Download dataset (sample of image, coco.names, yolov3.cfg, yolov3.weights).
```shell
./download_data.sh
#build program
go build main.go
#run it
./main -configFile yolov3.cfg --dataConfigFile coco.data -imageFile sample.jpg -weightsFile yolov3.weights
```
Note: you don't need *coco.data* file anymore, because sh-script above does insert *coco.names* into 'names' filed in *yolov3.cfg* file (so AlexeyAB's fork can deal with it properly)
So last rows in yolov3.cfg file will look like:
```bash
......
[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=80
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1
names = coco.names # this is path to coco.names file
```
Build and run program
```
go build main.go && ./main --configFile=yolov3.cfg --weightsFile=yolov3.weights --imageFile=sample.jpg
```
Output should be something like this:
```shell
truck (7): 95.6232% | start point: (78,69) | end point: (222, 291)
truck (7): 81.5451% | start point: (0,114) | end point: (90, 329)
car (2): 99.8129% | start point: (269,192) | end point: (421, 323)
car (2): 99.6615% | start point: (567,188) | end point: (743, 329)
car (2): 99.5795% | start point: (425,196) | end point: (544, 309)
car (2): 96.5765% | start point: (678,185) | end point: (797, 320)
car (2): 91.5156% | start point: (391,209) | end point: (441, 291)
car (2): 88.1737% | start point: (507,193) | end point: (660, 324)
car (2): 83.6209% | start point: (71,199) | end point: (102, 281)
bicycle (1): 59.4000% | start point: (183,276) | end point: (257, 407)
person (0): 96.3393% | start point: (142,119) | end point: (285, 356)
truck (7): 49.5197% | start point: (0,136) | end point: (85, 311)
car (2): 36.3747% | start point: (95,152) | end point: (186, 283)
truck (7): 48.4384% | start point: (95,152) | end point: (186, 283)
truck (7): 45.6590% | start point: (694,178) | end point: (798, 310)
car (2): 76.8379% | start point: (1,145) | end point: (84, 324)
truck (7): 25.5731% | start point: (107,89) | end point: (215, 263)
car (2): 99.8783% | start point: (511,185) | end point: (748, 328)
car (2): 99.8194% | start point: (261,189) | end point: (427, 322)
car (2): 99.6408% | start point: (426,197) | end point: (539, 311)
car (2): 74.5610% | start point: (692,186) | end point: (796, 316)
car (2): 72.8053% | start point: (388,206) | end point: (437, 276)
bicycle (1): 72.2932% | start point: (178,270) | end point: (268, 406)
person (0): 97.3026% | start point: (143,135) | end point: (268, 343)
```
## Documentation
See go-darknet's API documentation at [GoDoc].
## License
go-darknet follows [Darknet]'s [license].
[Darknet]: https://github.com/pjreddie/darknet
[license]: https://github.com/pjreddie/darknet/blob/master/LICENSE
[darknet.h]: https://github.com/pjreddie/darknet/blob/master/include/darknet.h
[include/darknet.h]: https://github.com/pjreddie/darknet/blob/master/include/darknet.h
[Makefile]: https://github.com/pjreddie/darknet/blob/master/Makefile
[darknet.h]: https://github.com/AlexeyAB/darknet/blob/master/include/darknet.h
[include/darknet.h]: https://github.com/AlexeyAB/darknet/blob/master/include/darknet.h
[Makefile]: https://github.com/alexeyab/darknet/blob/master/Makefile
[example]: /example
[GoDoc]: https://godoc.org/github.com/LdDl/go-darknet

8
classes.c Normal file
View File

@@ -0,0 +1,8 @@
#include <stdlib.h>
char *get_class_name(char **names, int index, int names_len) {
if (index >= names_len) {
return NULL;
}
return names[index];
}

14
classes.go Normal file
View File

@@ -0,0 +1,14 @@
package darknet
// #include <stdlib.h>
// #include "classes.h"
import "C"
func makeClassNames(names **C.char, classes int) []string {
out := make([]string, classes)
for i := 0; i < classes; i++ {
n := C.get_class_name(names, C.int(i), C.int(classes))
out[i] = C.GoString(n)
}
return out
}

3
classes.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
extern char *get_class_name(char **names, int index, int names_len);

View File

@@ -1,24 +0,0 @@
#include <stdlib.h>
#include <darknet.h>
void free_class_names(char **names)
{
free(names);
}
char ** read_class_names(char *data_cfg)
{
list *options = read_data_cfg(data_cfg);
char *name_list = option_find_str(options, "names", "data/names.list");
return get_labels(name_list);
}
char * get_class_name(char **names, int index, int names_len)
{
if (index >= names_len) {
return NULL;
}
return names[index];
}

View File

@@ -1,28 +0,0 @@
package darknet
// #include <stdlib.h>
// #include "classnames.h"
import "C"
import "unsafe"
func freeClassNames(names **C.char) {
C.free_class_names(names)
}
func loadClassNames(dataConfigFile string) **C.char {
d := C.CString(dataConfigFile)
defer C.free(unsafe.Pointer(d))
return C.read_class_names(d)
}
func makeClassNames(names **C.char, classes int) []string {
out := make([]string, classes)
for i := 0; i < classes; i++ {
n := C.get_class_name(names, C.int(i), C.int(classes))
s := C.GoString(n)
out[i] = s
}
return out
}

View File

@@ -1,5 +0,0 @@
#pragma once
extern void free_class_names(char **names);
extern char ** read_class_names(char *data_cfg);
extern char * get_class_name(char **names, int index, int names_len);

View File

@@ -1,5 +1,5 @@
package darknet
// #cgo CFLAGS: -I/usr/local/include -I/usr/local/cuda/include
// #cgo LDFLAGS: -L/usr/local/lib -ldarknet -lm
// #cgo LDFLAGS: -L/usr/lib -ldarknet -lm
import "C"

View File

@@ -1,19 +1,17 @@
#include <darknet.h>
detection * get_detection(detection *dets, int index, int dets_len)
{
#include "detection.h"
detection *get_detection(detection *dets, int index, int dets_len) {
if (index >= dets_len) {
return NULL;
}
return dets + index;
}
float get_detection_probability(detection *det, int index, int prob_len)
{
float get_detection_probability(detection *det, int index, int prob_len) {
if (index >= prob_len) {
return .0;
}
return det->prob[index];
}
}

View File

@@ -25,19 +25,15 @@ type DetectionResult struct {
OverallTimeTaken time.Duration
}
func makeDetection(img *Image, det *C.detection, threshold float32, classes int,
classNames []string) *Detection {
func makeDetection(img *DarknetImage, det *C.detection, threshold float32, classes int, classNames []string) *Detection {
if det == nil {
return &Detection{}
}
dClassIDs := make([]int, 0)
dClassNames := make([]string, 0)
dProbs := make([]float32, 0)
for i := 0; i < int(classes); i++ {
dProb := float32(
C.get_detection_probability(det, C.int(i), C.int(classes)))
dProb := float32(C.get_detection_probability(det, C.int(i), C.int(classes)))
if dProb > threshold {
dClassIDs = append(dClassIDs, i)
cN := classNames[i]
@@ -45,7 +41,6 @@ func makeDetection(img *Image, det *C.detection, threshold float32, classes int,
dProbs = append(dProbs, dProb*100)
}
}
fImgW := C.float(img.Width)
fImgH := C.float(img.Height)
halfRatioW := det.bbox.w / 2.0
@@ -66,19 +61,16 @@ func makeDetection(img *Image, det *C.detection, threshold float32, classes int,
ClassNames: dClassNames,
Probabilities: dProbs,
}
return &out
}
func makeDetections(img *Image, detections *C.detection, detectionsLength int,
threshold float32, classes int, classNames []string) []*Detection {
func makeDetections(img *DarknetImage, detections *C.detection, detectionsLength int, threshold float32, classes int, classNames []string) []*Detection {
// Make list of detection objects.
ds := make([]*Detection, detectionsLength)
for i := 0; i < int(detectionsLength); i++ {
det := C.get_detection(detections, C.int(i), C.int(classes))
det := C.get_detection(detections, C.int(i), C.int(detectionsLength))
d := makeDetection(img, det, threshold, classes, classNames)
ds[i] = d
}
return ds
}

View File

@@ -2,5 +2,5 @@
#include <darknet.h>
extern detection * get_detection(detection *dets, int index, int dets_len);
extern detection *get_detection(detection *dets, int index, int dets_len);
extern float get_detection_probability(detection *det, int index, int prob_len);

View File

@@ -2,33 +2,53 @@
This is an example Go application which uses go-darknet.
## Install
```shell
go get github.com/LdDl/go-darknet
go install github.com/LdDl/go-darknet/example
# Alternatively
go build github.com/LdDl/go-darknet/example
```
An executable named `example` should be in your `$GOPATH/bin`, if using
`go install`; otherwise it will be in your current working directory (`$PWD`),
if using `go build`.
## Run
Navigate to example folder:
```shell
$GOPATH/bin/example
cd $GOPATH/github.com/LdDl/go-darknet/example
```
or
```go
go run main.go -configFile=yolov3-320.cfg -dataConfigFile=coco.data -imageFile=sample.jpg -weightsFile=yolov3.weights
Download dataset (sample of image, coco.names, yolov3.cfg, yolov3.weights).
```shell
./download_data.sh
```
Note: you don't need *coco.data* file anymore, because script below does insert *coco.names* into 'names' filed in *yolov3.cfg* file (so AlexeyAB's fork can deal with it properly)
So last rows in yolov3.cfg file will look like:
```bash
......
[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=80
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1
names = coco.names # this is path to coco.names file
```
Please ensure that `libdarknet.so` is in your `$LD_LIBRARY_PATH`.
## Notes
Build and run program
```
go build main.go && ./main --configFile=yolov3.cfg --weightsFile=yolov3.weights --imageFile=sample.jpg
```
Note that the bounding boxes' values are ratios. To get the actual values, use
the ratios and multiply with either the image's width or height, depending on
which ratio is used.
Output should be something like this:
```shell
truck (7): 49.5197% | start point: (0,136) | end point: (85, 311)
car (2): 36.3747% | start point: (95,152) | end point: (186, 283)
truck (7): 48.4384% | start point: (95,152) | end point: (186, 283)
truck (7): 45.6590% | start point: (694,178) | end point: (798, 310)
car (2): 76.8379% | start point: (1,145) | end point: (84, 324)
truck (7): 25.5731% | start point: (107,89) | end point: (215, 263)
car (2): 99.8783% | start point: (511,185) | end point: (748, 328)
car (2): 99.8194% | start point: (261,189) | end point: (427, 322)
car (2): 99.6408% | start point: (426,197) | end point: (539, 311)
car (2): 74.5610% | start point: (692,186) | end point: (796, 316)
car (2): 72.8053% | start point: (388,206) | end point: (437, 276)
bicycle (1): 72.2932% | start point: (178,270) | end point: (268, 406)
person (0): 97.3026% | start point: (143,135) | end point: (268, 343)
```

View File

@@ -1,6 +1,5 @@
wget --output-document=sample.jpg https://cdn-images-1.medium.com/max/800/1*EYFejGUjvjPcc4PZTwoufw.jpeg
wget --output-document=coco.names https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names
wget --output-document=coco.data https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/coco.data
sed -i 's#data/coco.names#coco.names#g' coco.data
wget --output-document=yolov3.cfg https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg
wget --output-document=coco.names https://raw.githubusercontent.com/AlexeyAB/darknet/master/data/coco.names
wget --output-document=yolov3.cfg https://raw.githubusercontent.com/AlexeyAB/darknet/master/cfg/yolov3.cfg
sed -i -e "\$anames = coco.names" yolov3.cfg
wget --output-document=yolov3.weights https://pjreddie.com/media/files/yolov3.weights

View File

@@ -1,15 +1,19 @@
package main
import (
"bytes"
"flag"
"fmt"
"image"
"image/jpeg"
"log"
"math"
"os"
darknet "github.com/LdDl/go-darknet"
"github.com/disintegration/imaging"
)
var dataConfigFile = flag.String("dataConfigFile", "",
"Path to data configuration file. Example: cfg/coco.data")
var configFile = flag.String("configFile", "",
"Path to network layer configuration file. Example: cfg/yolov3.cfg")
var weightsFile = flag.String("weightsFile", "",
@@ -24,7 +28,7 @@ func printError(err error) {
func main() {
flag.Parse()
if *dataConfigFile == "" || *configFile == "" || *weightsFile == "" ||
if *configFile == "" || *weightsFile == "" ||
*imageFile == "" {
flag.Usage()
@@ -33,10 +37,9 @@ func main() {
n := darknet.YOLONetwork{
GPUDeviceIndex: 0,
DataConfigurationFile: *dataConfigFile,
NetworkConfigurationFile: *configFile,
WeightsFile: *weightsFile,
Threshold: .5,
Threshold: .25,
}
if err := n.Init(); err != nil {
printError(err)
@@ -44,14 +47,23 @@ func main() {
}
defer n.Close()
img, err := darknet.ImageFromPath(*imageFile)
infile, err := os.Open(*imageFile)
if err != nil {
printError(err)
return
panic(err.Error())
}
defer infile.Close()
src, err := jpeg.Decode(infile)
if err != nil {
panic(err.Error())
}
defer img.Close()
dr, err := n.Detect(img)
imgDarknet, err := darknet.Image2Float32(src)
if err != nil {
panic(err.Error())
}
defer imgDarknet.Close()
dr, err := n.Detect(imgDarknet)
if err != nil {
printError(err)
return
@@ -60,7 +72,6 @@ func main() {
log.Println("Network-only time taken:", dr.NetworkOnlyTimeTaken)
log.Println("Overall time taken:", dr.OverallTimeTaken, len(dr.Detections))
for _, d := range dr.Detections {
for i := range d.ClassIDs {
bBox := d.BoundingBox
fmt.Printf("%s (%d): %.4f%% | start point: (%d,%d) | end point: (%d, %d)\n",
@@ -69,6 +80,43 @@ func main() {
bBox.StartPoint.X, bBox.StartPoint.Y,
bBox.EndPoint.X, bBox.EndPoint.Y,
)
// Uncomment code below if you want save cropped objects to files
// minX, minY := float64(bBox.StartPoint.X), float64(bBox.StartPoint.Y)
// maxX, maxY := float64(bBox.EndPoint.X), float64(bBox.EndPoint.Y)
// rect := image.Rect(round(minX), round(minY), round(maxX), round(maxY))
// err := saveToFile(src, rect, fmt.Sprintf("crop_%d.jpeg", i))
// if err != nil {
// fmt.Println(err)
// return
// }
}
}
}
func imageToBytes(img image.Image) ([]byte, error) {
buf := new(bytes.Buffer)
err := jpeg.Encode(buf, img, nil)
return buf.Bytes(), err
}
func round(v float64) int {
if v >= 0 {
return int(math.Floor(v + 0.5))
}
return int(math.Ceil(v - 0.5))
}
func saveToFile(imgSrc image.Image, bbox image.Rectangle, fname string) error {
rectcropimg := imaging.Crop(imgSrc, bbox)
f, err := os.Create(fname)
if err != nil {
return err
}
defer f.Close()
err = jpeg.Encode(f, rectcropimg, nil)
if err != nil {
return err
}
return nil
}

13
image.c Normal file
View File

@@ -0,0 +1,13 @@
#include <darknet.h>
void fill_image_f32(image* im, int w, int h, int c, float* data) {
int i;
for (i = 0; i < w*h*c; i++) {
im->data[i] = data[i];
}
}
void set_data_f32_val(float* data, int index, float value) {
data[index] = value;
}

View File

@@ -1,66 +1,64 @@
package darknet
// #include <darknet.h>
// #include <stdlib.h>
// #include "image.h"
import "C"
import (
"errors"
"image"
"unsafe"
"golang.org/x/image/draw"
)
// Image represents the image buffer.
type Image struct {
// DarknetImage represents the image buffer.
type DarknetImage struct {
Width int
Height int
image C.image
image C.image
}
var (
errUnableToLoadImage = errors.New("unable to load image")
)
// Close and release resources.
func (img *Image) Close() error {
func (img *DarknetImage) Close() error {
C.free_image(img.image)
return nil
}
// ImageFromPath reads image file specified by path.
func ImageFromPath(path string) (*Image, error) {
p := C.CString(path)
defer C.free(unsafe.Pointer(p))
img := Image{
image: C.load_image_color(p, 0, 0),
// https://stackoverflow.com/questions/33186783/get-a-pixel-array-from-from-golang-image-image/59747737#59747737
func imgTofloat32(src image.Image) []float32 {
bounds := src.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
srcRGBA := image.NewRGBA(src.Bounds())
draw.Copy(srcRGBA, image.Point{}, src, src.Bounds(), draw.Src, nil)
ans := []float32{}
red := []float32{}
green := []float32{}
blue := []float32{}
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
idxSource := (y*width + x) * 4
pix := srcRGBA.Pix[idxSource : idxSource+4]
rpix, gpix, bpix := float32(pix[0])/257.0, float32(pix[1])/257.0, float32(pix[2])/257.0
red = append(red, rpix)
green = append(green, gpix)
blue = append(blue, bpix)
}
}
if img.image.data == nil {
return nil, errUnableToLoadImage
}
img.Width = int(img.image.w)
img.Height = int(img.image.h)
return &img, nil
ans = append(ans, red...)
ans = append(ans, green...)
ans = append(ans, blue...)
return ans
}
// ImageFromMemory reads image file data represented by the specified byte
// slice.
func ImageFromMemory(buf []byte) (*Image, error) {
cBuf := C.CBytes(buf)
defer C.free(cBuf)
img := Image{
image: C.load_image_from_memory_color((*C.uchar)(cBuf),
C.int(len(buf)), 0, 0),
// Image2Float32 Returns []float32 representation of image.Image
func Image2Float32(img image.Image) (*DarknetImage, error) {
ans := imgTofloat32(img)
width := img.Bounds().Dx()
height := img.Bounds().Dy()
imgDarknet := &DarknetImage{
Width: width,
Height: height,
image: C.make_image(C.int(width), C.int(height), 3),
}
if img.image.data == nil {
return nil, errUnableToLoadImage
}
img.Width = int(img.image.w)
img.Height = int(img.image.h)
return &img, nil
C.fill_image_f32(&imgDarknet.image, C.int(width), C.int(height), 3, (*C.float)(unsafe.Pointer(&ans[0])))
return imgDarknet, nil
}

6
image.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include <darknet.h>
extern void fill_image_f32(image *im, int w, int h, int c, float* data);
extern void set_data_f32_val(float* data, int index, float value);

View File

@@ -4,19 +4,25 @@
#include "network.h"
int get_network_layer_classes(network *n, int index) {
return n->layers[index].classes;
}
#include "detection.h"
struct network_box_result perform_network_detect(network *n, image *img, int classes, float thresh, float hier_thresh, float nms) {
image sized = letterbox_image(*img, n->w, n->h);
struct network_box_result perform_network_detect(network *n, image *img, int classes, float thresh, float hier_thresh, float nms, int letter_box) {
image sized;
if (letter_box) {
sized = letterbox_image(*img, n->w, n->h);
} else {
sized = resize_image(*img, n->w, n->h);
}
struct network_box_result result = { NULL };
float *X = sized.data;
network_predict(n, X);
result.detections = get_network_boxes(n, img->w, img->h,thresh, hier_thresh, 0, 1, &result.detections_len);
network_predict(*n, X);
int nboxes = 0;
detection *dets = get_network_boxes(n, img->w, img->h, thresh, hier_thresh, 0, 1, &nboxes, letter_box);
result.detections = get_network_boxes(n, img->w, img->h, thresh, hier_thresh, 0, 1, &result.detections_len, letter_box);
if (nms) {
do_nms_sort(result.detections, result.detections_len, classes, nms);
}
free_image(sized);
return result;
}

View File

@@ -15,7 +15,6 @@ import (
// YOLONetwork represents a neural network using YOLO.
type YOLONetwork struct {
GPUDeviceIndex int
DataConfigurationFile string
NetworkConfigurationFile string
WeightsFile string
Threshold float32
@@ -29,8 +28,8 @@ type YOLONetwork struct {
}
var (
errNetworkNotInit = errors.New("network not initialised")
errUnableToInitNetwork = errors.New("unable to initialise")
errNetworkNotInit = errors.New("Network not initialised")
errUnableToInitNetwork = errors.New("Unable to initialise")
)
// Init the network.
@@ -39,29 +38,18 @@ func (n *YOLONetwork) Init() error {
defer C.free(unsafe.Pointer(nCfg))
wFile := C.CString(n.WeightsFile)
defer C.free(unsafe.Pointer(wFile))
// GPU device ID must be set before `load_network()` is invoked.
C.cuda_set_device(C.int(n.GPUDeviceIndex))
n.cNet = C.load_network(nCfg, wFile, 0)
if n.cNet == nil {
return errUnableToInitNetwork
}
C.set_batch_network(n.cNet, 1)
C.srand(2222222)
// Currently, hierarchal threshold is always 0.5.
n.hierarchalThreshold = .5
// Currently NMS is always 0.4.
n.nms = .4
n.Classes = int(C.get_network_layer_classes(n.cNet, n.cNet.n-1))
cClassNames := loadClassNames(n.DataConfigurationFile)
defer freeClassNames(cClassNames)
n.ClassNames = makeClassNames(cClassNames, n.Classes)
n.hierarchalThreshold = 0.5
n.nms = 0.45
metadata := C.get_metadata(nCfg)
n.Classes = int(metadata.classes)
n.ClassNames = makeClassNames(metadata.names, n.Classes)
return nil
}
@@ -70,34 +58,26 @@ func (n *YOLONetwork) Close() error {
if n.cNet == nil {
return errNetworkNotInit
}
C.free_network(n.cNet)
C.free_network(*n.cNet)
n.cNet = nil
return nil
}
// Detect specified image.
func (n *YOLONetwork) Detect(img *Image) (*DetectionResult, error) {
// Detect specified image
func (n *YOLONetwork) Detect(img *DarknetImage) (*DetectionResult, error) {
if n.cNet == nil {
return nil, errNetworkNotInit
}
startTime := time.Now()
result := C.perform_network_detect(n.cNet, &img.image, C.int(n.Classes),
C.float(n.Threshold), C.float(n.hierarchalThreshold), C.float(n.nms))
result := C.perform_network_detect(n.cNet, &img.image, C.int(n.Classes), C.float(n.Threshold), C.float(n.hierarchalThreshold), C.float(n.nms), C.int(0))
endTime := time.Now()
defer C.free_detections(result.detections, result.detections_len)
ds := makeDetections(img, result.detections, int(result.detections_len),
n.Threshold, n.Classes, n.ClassNames)
ds := makeDetections(img, result.detections, int(result.detections_len), n.Threshold, n.Classes, n.ClassNames)
endTimeOverall := time.Now()
out := DetectionResult{
Detections: ds,
NetworkOnlyTimeTaken: endTime.Sub(startTime),
OverallTimeTaken: endTimeOverall.Sub(startTime),
}
return &out, nil
}

View File

@@ -8,4 +8,4 @@ struct network_box_result {
};
extern int get_network_layer_classes(network *n, int index);
extern struct network_box_result perform_network_detect(network *n, image *img, int classes, float thresh, float hier_thresh, float nms);
extern struct network_box_result perform_network_detect(network *n, image *img, int classes, float thresh, float hier_thresh, float nms, int letter_box);