From de6908e7195e87b7dfef8c3d1d1d2fc3a8e2410a Mon Sep 17 00:00:00 2001 From: Jeroen Staab Date: Mon, 24 Dec 2018 14:18:38 +0100 Subject: [PATCH 1/8] Added some new models/weights --- image.darknet/R/darknet_models.R | 3 +- .../inst/include/darknet/cfg/yolov2.cfg | 258 ++++++ .../inst/include/darknet/cfg/yolov3.cfg | 789 ++++++++++++++++++ 3 files changed, 1049 insertions(+), 1 deletion(-) create mode 100644 image.darknet/inst/include/darknet/cfg/yolov2.cfg create mode 100644 image.darknet/inst/include/darknet/cfg/yolov3.cfg diff --git a/image.darknet/R/darknet_models.R b/image.darknet/R/darknet_models.R index 7ce0ca1..19825ec 100644 --- a/image.darknet/R/darknet_models.R +++ b/image.darknet/R/darknet_models.R @@ -136,7 +136,8 @@ image_darknet_model <- function(type = c("classify", "detect"), model, weights, labels, resize=TRUE){ if(model %in% c("tiny.cfg", "alexnet.cfg", "darknet.cfg", "vgg-16.cfg", "extraction.cfg", "darknet19.cfg", "darknet19_448.cfg", - "yolo.cfg", "tiny-yolo.cfg", "yolo-voc", "tiny-yolo-voc.cfg")){ + "yolo.cfg", "tiny-yolo.cfg", "yolo-voc", "tiny-yolo-voc.cfg", + "yolov2.cfg", "yolov2-voc.cfg", "yolov3.cfg", "yolov3-voc.cfg")){ model <- system.file(package="image.darknet", "include", "darknet", "cfg", model) } stopifnot(file.exists(model)) diff --git a/image.darknet/inst/include/darknet/cfg/yolov2.cfg b/image.darknet/inst/include/darknet/cfg/yolov2.cfg new file mode 100644 index 0000000..088edf8 --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/yolov2.cfg @@ -0,0 +1,258 @@ +[net] +# Testing +batch=1 +subdivisions=1 +# Training +# batch=64 +# subdivisions=8 +width=608 +height=608 +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 = 500200 +policy=steps +steps=400000,450000 +scales=.1,.1 + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + + +####### + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[route] +layers=-9 + +[convolutional] +batch_normalize=1 +size=1 +stride=1 +pad=1 +filters=64 +activation=leaky + +[reorg] +stride=2 + +[route] +layers=-1,-4 + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=425 +activation=linear + + +[region] +anchors = 0.57273, 0.677385, 1.87446, 2.06253, 3.33843, 5.47434, 7.88282, 3.52778, 9.77052, 9.16828 +bias_match=1 +classes=80 +coords=4 +num=5 +softmax=1 +jitter=.3 +rescore=1 + +object_scale=5 +noobject_scale=1 +class_scale=1 +coord_scale=1 + +absolute=1 +thresh = .6 +random=1 diff --git a/image.darknet/inst/include/darknet/cfg/yolov3.cfg b/image.darknet/inst/include/darknet/cfg/yolov3.cfg new file mode 100644 index 0000000..938ffff --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/yolov3.cfg @@ -0,0 +1,789 @@ +[net] +# Testing +# batch=1 +# subdivisions=1 +# Training +batch=64 +subdivisions=16 +width=608 +height=608 +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 = 500200 +policy=steps +steps=400000,450000 +scales=.1,.1 + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +# Downsample + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +###################### + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=255 +activation=linear + + +[yolo] +mask = 6,7,8 +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 + + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 61 + + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=255 +activation=linear + + +[yolo] +mask = 3,4,5 +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 + + + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 36 + + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=255 +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=80 +num=9 +jitter=.3 +ignore_thresh = .7 +truth_thresh = 1 +random=1 + From 0255d3cb6ebe66777a1589209518811a38a088f9 Mon Sep 17 00:00:00 2001 From: Jeroen Staab Date: Mon, 24 Dec 2018 14:56:09 +0100 Subject: [PATCH 2/8] Updated src/ with src/ from YOLO repository --- image.darknet/src/activation_kernels.cu | 87 +- image.darknet/src/activation_layer.c | 20 +- image.darknet/src/activation_layer.h | 8 +- image.darknet/src/activations.c | 7 + image.darknet/src/activations.h | 11 +- image.darknet/src/art.c | 77 - image.darknet/src/avgpool_layer.c | 8 +- image.darknet/src/avgpool_layer.h | 8 +- image.darknet/src/avgpool_layer_kernels.cu | 8 +- image.darknet/src/batchnorm_layer.c | 200 +- image.darknet/src/batchnorm_layer.h | 8 +- image.darknet/src/blas.c | 147 +- image.darknet/src/blas.h | 70 +- image.darknet/src/blas_kernels.cu | 408 ++- image.darknet/src/box.c | 168 +- image.darknet/src/box.h | 10 +- image.darknet/src/captcha.c | 364 --- image.darknet/src/cifar.c | 277 -- image.darknet/src/classifier.c | 1167 -------- image.darknet/src/classifier.h | 1 - image.darknet/src/coco.c | 388 --- image.darknet/src/col2im.h | 2 +- image.darknet/src/col2im_kernels.cu | 2 +- image.darknet/src/compare.c | 2 +- image.darknet/src/connected_layer.c | 179 +- image.darknet/src/connected_layer.h | 22 +- image.darknet/src/convolutional_kernels.cu | 271 +- image.darknet/src/convolutional_layer.c | 254 +- image.darknet/src/convolutional_layer.h | 18 +- image.darknet/src/cost_layer.c | 60 +- image.darknet/src/cost_layer.h | 8 +- image.darknet/src/crnn_layer.c | 106 +- image.darknet/src/crnn_layer.h | 12 +- image.darknet/src/crop_layer.c | 10 +- image.darknet/src/crop_layer.h | 4 +- image.darknet/src/crop_layer_kernels.cu | 32 +- image.darknet/src/cuda.c | 22 +- image.darknet/src/cuda.h | 19 +- image.darknet/src/darknet.c | 452 --- image.darknet/src/data.c | 591 +++- image.darknet/src/data.h | 70 +- image.darknet/src/deconvolutional_kernels.cu | 148 +- image.darknet/src/deconvolutional_layer.c | 292 +- image.darknet/src/deconvolutional_layer.h | 29 +- image.darknet/src/demo.c | 416 ++- image.darknet/src/demo.h | 5 +- image.darknet/src/detection_layer.c | 88 +- image.darknet/src/detection_layer.h | 9 +- image.darknet/src/detector.c | 552 ---- image.darknet/src/dice.c | 118 - image.darknet/src/dropout_layer.c | 16 +- image.darknet/src/dropout_layer.h | 8 +- image.darknet/src/dropout_layer_kernels.cu | 12 +- image.darknet/src/gemm.c | 62 +- image.darknet/src/gemm.h | 2 +- image.darknet/src/go.c | 833 ------ image.darknet/src/gru_layer.c | 461 +-- image.darknet/src/gru_layer.h | 14 +- image.darknet/src/im2col.h | 2 +- image.darknet/src/im2col_kernels.cu | 2 +- image.darknet/src/image.c | 561 ++-- image.darknet/src/image.h | 66 +- image.darknet/src/image_opencv.cpp | 135 + image.darknet/src/iseg_layer.c | 225 ++ image.darknet/src/iseg_layer.h | 19 + image.darknet/src/l2norm_layer.c | 63 + image.darknet/src/l2norm_layer.h | 15 + image.darknet/src/layer.c | 3 +- image.darknet/src/layer.h | 272 +- image.darknet/src/list.h | 15 +- image.darknet/src/local_layer.c | 90 +- image.darknet/src/local_layer.h | 12 +- image.darknet/src/logistic_layer.c | 71 + image.darknet/src/logistic_layer.h | 15 + image.darknet/src/lstm_layer.c | 626 ++++ image.darknet/src/lstm_layer.h | 20 + image.darknet/src/matrix.c | 15 + image.darknet/src/matrix.h | 13 +- image.darknet/src/maxpool_layer.c | 24 +- image.darknet/src/maxpool_layer.h | 8 +- image.darknet/src/maxpool_layer_kernels.cu | 24 +- image.darknet/src/network.c | 933 ++++-- image.darknet/src/network.h | 116 +- image.darknet/src/network_kernels.cu | 408 --- image.darknet/src/nightmare.c | 308 -- image.darknet/src/normalization_layer.c | 41 +- image.darknet/src/normalization_layer.h | 8 +- image.darknet/src/option_list.c | 17 + image.darknet/src/option_list.h | 4 - image.darknet/src/parser.c | 448 ++- image.darknet/src/parser.h | 6 +- image.darknet/src/region_layer.c | 396 ++- image.darknet/src/region_layer.h | 12 +- image.darknet/src/reorg_layer.c | 101 +- image.darknet/src/reorg_layer.h | 10 +- image.darknet/src/rnn.c | 492 ---- image.darknet/src/rnn_layer.c | 173 +- image.darknet/src/rnn_layer.h | 14 +- image.darknet/src/rnn_vid.c | 211 -- image.darknet/src/route_layer.c | 21 +- image.darknet/src/route_layer.h | 8 +- image.darknet/src/shortcut_layer.c | 53 +- image.darknet/src/shortcut_layer.h | 9 +- image.darknet/src/softmax_layer.c | 67 +- image.darknet/src/softmax_layer.h | 8 +- image.darknet/src/stb_image.h | 2723 ++++++++++++------ image.darknet/src/stb_image_write.h | 1208 ++++++-- image.darknet/src/super.c | 131 - image.darknet/src/swag.c | 91 - image.darknet/src/tag.c | 153 - image.darknet/src/tree.c | 16 +- image.darknet/src/tree.h | 21 +- image.darknet/src/upsample_layer.c | 106 + image.darknet/src/upsample_layer.h | 15 + image.darknet/src/utils.c | 115 +- image.darknet/src/utils.h | 33 +- image.darknet/src/voxel.c | 169 -- image.darknet/src/writing.c | 150 - image.darknet/src/yolo.c | 355 --- image.darknet/src/yolo_layer.c | 374 +++ image.darknet/src/yolo_layer.h | 19 + 121 files changed, 9734 insertions(+), 10748 deletions(-) delete mode 100644 image.darknet/src/art.c delete mode 100644 image.darknet/src/captcha.c delete mode 100644 image.darknet/src/cifar.c delete mode 100644 image.darknet/src/classifier.c delete mode 100644 image.darknet/src/coco.c delete mode 100644 image.darknet/src/darknet.c delete mode 100644 image.darknet/src/detector.c delete mode 100644 image.darknet/src/dice.c delete mode 100644 image.darknet/src/go.c create mode 100644 image.darknet/src/image_opencv.cpp create mode 100644 image.darknet/src/iseg_layer.c create mode 100644 image.darknet/src/iseg_layer.h create mode 100644 image.darknet/src/l2norm_layer.c create mode 100644 image.darknet/src/l2norm_layer.h create mode 100644 image.darknet/src/logistic_layer.c create mode 100644 image.darknet/src/logistic_layer.h create mode 100644 image.darknet/src/lstm_layer.c create mode 100644 image.darknet/src/lstm_layer.h delete mode 100644 image.darknet/src/network_kernels.cu delete mode 100644 image.darknet/src/nightmare.c delete mode 100644 image.darknet/src/rnn.c delete mode 100644 image.darknet/src/rnn_vid.c delete mode 100644 image.darknet/src/super.c delete mode 100644 image.darknet/src/swag.c delete mode 100644 image.darknet/src/tag.c create mode 100644 image.darknet/src/upsample_layer.c create mode 100644 image.darknet/src/upsample_layer.h delete mode 100644 image.darknet/src/voxel.c delete mode 100644 image.darknet/src/writing.c delete mode 100644 image.darknet/src/yolo.c create mode 100644 image.darknet/src/yolo_layer.c create mode 100644 image.darknet/src/yolo_layer.h diff --git a/image.darknet/src/activation_kernels.cu b/image.darknet/src/activation_kernels.cu index 994e206..4dc5804 100644 --- a/image.darknet/src/activation_kernels.cu +++ b/image.darknet/src/activation_kernels.cu @@ -10,8 +10,8 @@ extern "C" { __device__ float lhtan_activate_kernel(float x) { - if(x < 0) return .001*x; - if(x > 1) return .001*(x-1) + 1; + if(x < 0) return .001f*x; + if(x > 1) return .001f*(x-1.f) + 1.f; return x; } __device__ float lhtan_gradient_kernel(float x) @@ -27,25 +27,26 @@ __device__ float hardtan_activate_kernel(float x) return x; } __device__ float linear_activate_kernel(float x){return x;} -__device__ float logistic_activate_kernel(float x){return 1./(1. + exp(-x));} -__device__ float loggy_activate_kernel(float x){return 2./(1. + exp(-x)) - 1;} +__device__ float logistic_activate_kernel(float x){return 1.f/(1.f + expf(-x));} +__device__ float loggy_activate_kernel(float x){return 2.f/(1.f + expf(-x)) - 1;} __device__ float relu_activate_kernel(float x){return x*(x>0);} -__device__ float elu_activate_kernel(float x){return (x >= 0)*x + (x < 0)*(exp(x)-1);} -__device__ float relie_activate_kernel(float x){return (x>0) ? x : .01*x;} -__device__ float ramp_activate_kernel(float x){return x*(x>0)+.1*x;} -__device__ float leaky_activate_kernel(float x){return (x>0) ? x : .1*x;} -__device__ float tanh_activate_kernel(float x){return (2/(1 + exp(-2*x)) - 1);} +__device__ float elu_activate_kernel(float x){return (x >= 0)*x + (x < 0)*(expf(x)-1);} +__device__ float selu_activate_kernel(float x){return (x >= 0)*1.0507f*x + (x < 0)*1.0507f*1.6732f*(expf(x)-1);} +__device__ float relie_activate_kernel(float x){return (x>0) ? x : .01f*x;} +__device__ float ramp_activate_kernel(float x){return x*(x>0)+.1f*x;} +__device__ float leaky_activate_kernel(float x){return (x>0) ? x : .1f*x;} +__device__ float tanh_activate_kernel(float x){return (2.f/(1 + expf(-2*x)) - 1);} __device__ float plse_activate_kernel(float x) { - if(x < -4) return .01 * (x + 4); - if(x > 4) return .01 * (x - 4) + 1; - return .125*x + .5; + if(x < -4) return .01f * (x + 4); + if(x > 4) return .01f * (x - 4) + 1; + return .125f*x + .5f; } __device__ float stair_activate_kernel(float x) { - int n = floor(x); - if (n%2 == 0) return floor(x/2.); - else return (x - n) + floor(x/2.); + int n = floorf(x); + if (n%2 == 0) return floorf(x/2); + else return (x - n) + floorf(x/2); } @@ -58,19 +59,20 @@ __device__ float linear_gradient_kernel(float x){return 1;} __device__ float logistic_gradient_kernel(float x){return (1-x)*x;} __device__ float loggy_gradient_kernel(float x) { - float y = (x+1.)/2.; + float y = (x+1)/2; return 2*(1-y)*y; } __device__ float relu_gradient_kernel(float x){return (x>0);} __device__ float elu_gradient_kernel(float x){return (x >= 0) + (x < 0)*(x + 1);} -__device__ float relie_gradient_kernel(float x){return (x>0) ? 1 : .01;} -__device__ float ramp_gradient_kernel(float x){return (x>0)+.1;} -__device__ float leaky_gradient_kernel(float x){return (x>0) ? 1 : .1;} +__device__ float selu_gradient_kernel(float x){return (x >= 0)*1.0507 + (x < 0)*(x + 1.0507*1.6732);} +__device__ float relie_gradient_kernel(float x){return (x>0) ? 1 : .01f;} +__device__ float ramp_gradient_kernel(float x){return (x>0)+.1f;} +__device__ float leaky_gradient_kernel(float x){return (x>0) ? 1 : .1f;} __device__ float tanh_gradient_kernel(float x){return 1-x*x;} -__device__ float plse_gradient_kernel(float x){return (x < 0 || x > 1) ? .01 : .125;} +__device__ float plse_gradient_kernel(float x){return (x < 0 || x > 1) ? .01f : .125f;} __device__ float stair_gradient_kernel(float x) { - if (floor(x) == x) return 0; + if (floorf(x) == x) return 0; return 1; } @@ -87,6 +89,8 @@ __device__ float activate_kernel(float x, ACTIVATION a) return relu_activate_kernel(x); case ELU: return elu_activate_kernel(x); + case SELU: + return selu_activate_kernel(x); case RELIE: return relie_activate_kernel(x); case RAMP: @@ -120,6 +124,8 @@ __device__ float gradient_kernel(float x, ACTIVATION a) return relu_gradient_kernel(x); case ELU: return elu_gradient_kernel(x); + case SELU: + return selu_gradient_kernel(x); case RELIE: return relie_gradient_kernel(x); case RAMP: @@ -140,6 +146,41 @@ __device__ float gradient_kernel(float x, ACTIVATION a) return 0; } +__global__ void binary_gradient_array_kernel(float *x, float *dy, int n, int s, BINARY_ACTIVATION a, float *dx) +{ + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + int i = id % s; + int b = id / s; + float x1 = x[b*s + i]; + float x2 = x[b*s + s/2 + i]; + if(id < n) { + float de = dy[id]; + dx[b*s + i] = x2*de; + dx[b*s + s/2 + i] = x1*de; + } +} + +extern "C" void binary_gradient_array_gpu(float *x, float *dx, int n, int size, BINARY_ACTIVATION a, float *y) +{ + binary_gradient_array_kernel<<>>(x, dx, n/2, size, a, y); + check_error(cudaPeekAtLastError()); +} +__global__ void binary_activate_array_kernel(float *x, int n, int s, BINARY_ACTIVATION a, float *y) +{ + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + int i = id % s; + int b = id / s; + float x1 = x[b*s + i]; + float x2 = x[b*s + s/2 + i]; + if(id < n) y[id] = x1*x2; +} + +extern "C" void binary_activate_array_gpu(float *x, int n, int size, BINARY_ACTIVATION a, float *y) +{ + binary_activate_array_kernel<<>>(x, n/2, size, a, y); + check_error(cudaPeekAtLastError()); +} + __global__ void activate_array_kernel(float *x, int n, ACTIVATION a) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; @@ -152,13 +193,13 @@ __global__ void gradient_array_kernel(float *x, int n, ACTIVATION a, float *delt if(i < n) delta[i] *= gradient_kernel(x[i], a); } -extern "C" void activate_array_ongpu(float *x, int n, ACTIVATION a) +extern "C" void activate_array_gpu(float *x, int n, ACTIVATION a) { activate_array_kernel<<>>(x, n, a); check_error(cudaPeekAtLastError()); } -extern "C" void gradient_array_ongpu(float *x, int n, ACTIVATION a, float *delta) +extern "C" void gradient_array_gpu(float *x, int n, ACTIVATION a, float *delta) { gradient_array_kernel<<>>(x, n, a, delta); check_error(cudaPeekAtLastError()); diff --git a/image.darknet/src/activation_layer.c b/image.darknet/src/activation_layer.c index 3430dac..b4ba953 100644 --- a/image.darknet/src/activation_layer.c +++ b/image.darknet/src/activation_layer.c @@ -35,29 +35,29 @@ layer make_activation_layer(int batch, int inputs, ACTIVATION activation) return l; } -void forward_activation_layer(layer l, network_state state) +void forward_activation_layer(layer l, network net) { - copy_cpu(l.outputs*l.batch, state.input, 1, l.output, 1); + copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); activate_array(l.output, l.outputs*l.batch, l.activation); } -void backward_activation_layer(layer l, network_state state) +void backward_activation_layer(layer l, network net) { gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); - copy_cpu(l.outputs*l.batch, l.delta, 1, state.delta, 1); + copy_cpu(l.outputs*l.batch, l.delta, 1, net.delta, 1); } #ifdef GPU -void forward_activation_layer_gpu(layer l, network_state state) +void forward_activation_layer_gpu(layer l, network net) { - copy_ongpu(l.outputs*l.batch, state.input, 1, l.output_gpu, 1); - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); } -void backward_activation_layer_gpu(layer l, network_state state) +void backward_activation_layer_gpu(layer l, network net) { - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); - copy_ongpu(l.outputs*l.batch, l.delta_gpu, 1, state.delta, 1); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + copy_gpu(l.outputs*l.batch, l.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/src/activation_layer.h b/image.darknet/src/activation_layer.h index a09756a..42118a8 100644 --- a/image.darknet/src/activation_layer.h +++ b/image.darknet/src/activation_layer.h @@ -7,12 +7,12 @@ layer make_activation_layer(int batch, int inputs, ACTIVATION activation); -void forward_activation_layer(layer l, network_state state); -void backward_activation_layer(layer l, network_state state); +void forward_activation_layer(layer l, network net); +void backward_activation_layer(layer l, network net); #ifdef GPU -void forward_activation_layer_gpu(layer l, network_state state); -void backward_activation_layer_gpu(layer l, network_state state); +void forward_activation_layer_gpu(layer l, network net); +void backward_activation_layer_gpu(layer l, network net); #endif #endif diff --git a/image.darknet/src/activations.c b/image.darknet/src/activations.c index 0cbb2f5..da1a17a 100644 --- a/image.darknet/src/activations.c +++ b/image.darknet/src/activations.c @@ -16,6 +16,8 @@ char *get_activation_string(ACTIVATION a) return "relu"; case ELU: return "elu"; + case SELU: + return "selu"; case RELIE: return "relie"; case RAMP: @@ -46,6 +48,7 @@ ACTIVATION get_activation(char *s) if (strcmp(s, "loggy")==0) return LOGGY; if (strcmp(s, "relu")==0) return RELU; if (strcmp(s, "elu")==0) return ELU; + if (strcmp(s, "selu")==0) return SELU; if (strcmp(s, "relie")==0) return RELIE; if (strcmp(s, "plse")==0) return PLSE; if (strcmp(s, "hardtan")==0) return HARDTAN; @@ -72,6 +75,8 @@ float activate(float x, ACTIVATION a) return relu_activate(x); case ELU: return elu_activate(x); + case SELU: + return selu_activate(x); case RELIE: return relie_activate(x); case RAMP: @@ -113,6 +118,8 @@ float gradient(float x, ACTIVATION a) return relu_gradient(x); case ELU: return elu_gradient(x); + case SELU: + return selu_gradient(x); case RELIE: return relie_gradient(x); case RAMP: diff --git a/image.darknet/src/activations.h b/image.darknet/src/activations.h index 1c36ff5..9780d2c 100644 --- a/image.darknet/src/activations.h +++ b/image.darknet/src/activations.h @@ -1,12 +1,9 @@ #ifndef ACTIVATIONS_H #define ACTIVATIONS_H +#include "darknet.h" #include "cuda.h" #include "math.h" -typedef enum{ - LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN -}ACTIVATION; - ACTIVATION get_activation(char *s); char *get_activation_string(ACTIVATION a); @@ -15,8 +12,8 @@ float gradient(float x, ACTIVATION a); void gradient_array(const float *x, const int n, const ACTIVATION a, float *delta); void activate_array(float *x, const int n, const ACTIVATION a); #ifdef GPU -void activate_array_ongpu(float *x, int n, ACTIVATION a); -void gradient_array_ongpu(float *x, int n, ACTIVATION a, float *delta); +void activate_array_gpu(float *x, int n, ACTIVATION a); +void gradient_array_gpu(float *x, int n, ACTIVATION a, float *delta); #endif static inline float stair_activate(float x) @@ -36,6 +33,7 @@ static inline float logistic_activate(float x){return 1./(1. + exp(-x));} static inline float loggy_activate(float x){return 2./(1. + exp(-x)) - 1;} static inline float relu_activate(float x){return x*(x>0);} static inline float elu_activate(float x){return (x >= 0)*x + (x < 0)*(exp(x)-1);} +static inline float selu_activate(float x){return (x >= 0)*1.0507*x + (x < 0)*1.0507*1.6732*(exp(x)-1);} static inline float relie_activate(float x){return (x>0) ? x : .01*x;} static inline float ramp_activate(float x){return x*(x>0)+.1*x;} static inline float leaky_activate(float x){return (x>0) ? x : .1*x;} @@ -78,6 +76,7 @@ static inline float stair_gradient(float x) } static inline float relu_gradient(float x){return (x>0);} static inline float elu_gradient(float x){return (x >= 0) + (x < 0)*(x + 1);} +static inline float selu_gradient(float x){return (x >= 0)*1.0507 + (x < 0)*(x + 1.0507*1.6732);} static inline float relie_gradient(float x){return (x>0) ? 1 : .01;} static inline float ramp_gradient(float x){return (x>0)+.1;} static inline float leaky_gradient(float x){return (x>0) ? 1 : .1;} diff --git a/image.darknet/src/art.c b/image.darknet/src/art.c deleted file mode 100644 index 71d3719..0000000 --- a/image.darknet/src/art.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" -#include "option_list.h" -#include "blas.h" -#include "classifier.h" -#include - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -image get_image_from_stream(CvCapture *cap); -#endif - - -void demo_art(char *cfgfile, char *weightfile, int cam_index) -{ -#ifdef OPENCV - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - - srand(2222222); - CvCapture * cap; - - cap = cvCaptureFromCAM(cam_index); - - char *window = "ArtJudgementBot9000!!!"; - if(!cap) error("Couldn't connect to webcam.\n"); - cvNamedWindow(window, CV_WINDOW_NORMAL); - cvResizeWindow(window, 512, 512); - int i; - int idx[] = {37, 401, 434}; - int n = sizeof(idx)/sizeof(idx[0]); - - while(1){ - image in = get_image_from_stream(cap); - image in_s = resize_image(in, net.w, net.h); - show_image(in, window); - - float *p = network_predict(net, in_s.data); - - printf("\033[2J"); - printf("\033[1;1H"); - - float score = 0; - for(i = 0; i < n; ++i){ - float s = p[idx[i]]; - if (s > score) score = s; - } - score = score; - printf("I APPRECIATE THIS ARTWORK: %10.7f%%\n", score*100); - printf("["); - int upper = 30; - for(i = 0; i < upper; ++i){ - printf("%c", ((i+.5) < score*upper) ? 219 : ' '); - } - printf("]\n"); - - free_image(in_s); - free_image(in); - - cvWaitKey(1); - } -#endif -} - - -void run_art(int argc, char **argv) -{ - int cam_index = find_int_arg(argc, argv, "-c", 0); - char *cfg = argv[2]; - char *weights = argv[3]; - demo_art(cfg, weights, cam_index); -} - diff --git a/image.darknet/src/avgpool_layer.c b/image.darknet/src/avgpool_layer.c index b6932fe..83034db 100644 --- a/image.darknet/src/avgpool_layer.c +++ b/image.darknet/src/avgpool_layer.c @@ -37,7 +37,7 @@ void resize_avgpool_layer(avgpool_layer *l, int w, int h) l->inputs = h*w*l->c; } -void forward_avgpool_layer(const avgpool_layer l, network_state state) +void forward_avgpool_layer(const avgpool_layer l, network net) { int b,i,k; @@ -47,14 +47,14 @@ void forward_avgpool_layer(const avgpool_layer l, network_state state) l.output[out_index] = 0; for(i = 0; i < l.h*l.w; ++i){ int in_index = i + l.h*l.w*(k + b*l.c); - l.output[out_index] += state.input[in_index]; + l.output[out_index] += net.input[in_index]; } l.output[out_index] /= l.h*l.w; } } } -void backward_avgpool_layer(const avgpool_layer l, network_state state) +void backward_avgpool_layer(const avgpool_layer l, network net) { int b,i,k; @@ -63,7 +63,7 @@ void backward_avgpool_layer(const avgpool_layer l, network_state state) int out_index = k + b*l.c; for(i = 0; i < l.h*l.w; ++i){ int in_index = i + l.h*l.w*(k + b*l.c); - state.delta[in_index] += l.delta[out_index] / (l.h*l.w); + net.delta[in_index] += l.delta[out_index] / (l.h*l.w); } } } diff --git a/image.darknet/src/avgpool_layer.h b/image.darknet/src/avgpool_layer.h index f8329ae..3bd356c 100644 --- a/image.darknet/src/avgpool_layer.h +++ b/image.darknet/src/avgpool_layer.h @@ -11,12 +11,12 @@ typedef layer avgpool_layer; image get_avgpool_image(avgpool_layer l); avgpool_layer make_avgpool_layer(int batch, int w, int h, int c); void resize_avgpool_layer(avgpool_layer *l, int w, int h); -void forward_avgpool_layer(const avgpool_layer l, network_state state); -void backward_avgpool_layer(const avgpool_layer l, network_state state); +void forward_avgpool_layer(const avgpool_layer l, network net); +void backward_avgpool_layer(const avgpool_layer l, network net); #ifdef GPU -void forward_avgpool_layer_gpu(avgpool_layer l, network_state state); -void backward_avgpool_layer_gpu(avgpool_layer l, network_state state); +void forward_avgpool_layer_gpu(avgpool_layer l, network net); +void backward_avgpool_layer_gpu(avgpool_layer l, network net); #endif #endif diff --git a/image.darknet/src/avgpool_layer_kernels.cu b/image.darknet/src/avgpool_layer_kernels.cu index b7e2770..a7eca3a 100644 --- a/image.darknet/src/avgpool_layer_kernels.cu +++ b/image.darknet/src/avgpool_layer_kernels.cu @@ -43,19 +43,19 @@ __global__ void backward_avgpool_layer_kernel(int n, int w, int h, int c, float } } -extern "C" void forward_avgpool_layer_gpu(avgpool_layer layer, network_state state) +extern "C" void forward_avgpool_layer_gpu(avgpool_layer layer, network net) { size_t n = layer.c*layer.batch; - forward_avgpool_layer_kernel<<>>(n, layer.w, layer.h, layer.c, state.input, layer.output_gpu); + forward_avgpool_layer_kernel<<>>(n, layer.w, layer.h, layer.c, net.input_gpu, layer.output_gpu); check_error(cudaPeekAtLastError()); } -extern "C" void backward_avgpool_layer_gpu(avgpool_layer layer, network_state state) +extern "C" void backward_avgpool_layer_gpu(avgpool_layer layer, network net) { size_t n = layer.c*layer.batch; - backward_avgpool_layer_kernel<<>>(n, layer.w, layer.h, layer.c, state.delta, layer.delta_gpu); + backward_avgpool_layer_kernel<<>>(n, layer.w, layer.h, layer.c, net.delta_gpu, layer.delta_gpu); check_error(cudaPeekAtLastError()); } diff --git a/image.darknet/src/batchnorm_layer.c b/image.darknet/src/batchnorm_layer.c index b53548b..ebff387 100644 --- a/image.darknet/src/batchnorm_layer.c +++ b/image.darknet/src/batchnorm_layer.c @@ -1,3 +1,4 @@ +#include "convolutional_layer.h" #include "batchnorm_layer.h" #include "blas.h" #include @@ -5,55 +6,67 @@ layer make_batchnorm_layer(int batch, int w, int h, int c) { fprintf(stderr, "Batch Normalization Layer: %d x %d x %d image\n", w,h,c); - layer layer = {0}; - layer.type = BATCHNORM; - layer.batch = batch; - layer.h = layer.out_h = h; - layer.w = layer.out_w = w; - layer.c = layer.out_c = c; - layer.output = calloc(h * w * c * batch, sizeof(float)); - layer.delta = calloc(h * w * c * batch, sizeof(float)); - layer.inputs = w*h*c; - layer.outputs = layer.inputs; - - layer.scales = calloc(c, sizeof(float)); - layer.scale_updates = calloc(c, sizeof(float)); + layer l = {0}; + l.type = BATCHNORM; + l.batch = batch; + l.h = l.out_h = h; + l.w = l.out_w = w; + l.c = l.out_c = c; + l.output = calloc(h * w * c * batch, sizeof(float)); + l.delta = calloc(h * w * c * batch, sizeof(float)); + l.inputs = w*h*c; + l.outputs = l.inputs; + + l.scales = calloc(c, sizeof(float)); + l.scale_updates = calloc(c, sizeof(float)); + l.biases = calloc(c, sizeof(float)); + l.bias_updates = calloc(c, sizeof(float)); int i; for(i = 0; i < c; ++i){ - layer.scales[i] = 1; + l.scales[i] = 1; } - layer.mean = calloc(c, sizeof(float)); - layer.variance = calloc(c, sizeof(float)); + l.mean = calloc(c, sizeof(float)); + l.variance = calloc(c, sizeof(float)); - layer.rolling_mean = calloc(c, sizeof(float)); - layer.rolling_variance = calloc(c, sizeof(float)); + l.rolling_mean = calloc(c, sizeof(float)); + l.rolling_variance = calloc(c, sizeof(float)); - layer.forward = forward_batchnorm_layer; - layer.backward = backward_batchnorm_layer; + l.forward = forward_batchnorm_layer; + l.backward = backward_batchnorm_layer; #ifdef GPU - layer.forward_gpu = forward_batchnorm_layer_gpu; - layer.backward_gpu = backward_batchnorm_layer_gpu; + l.forward_gpu = forward_batchnorm_layer_gpu; + l.backward_gpu = backward_batchnorm_layer_gpu; + + l.output_gpu = cuda_make_array(l.output, h * w * c * batch); + l.delta_gpu = cuda_make_array(l.delta, h * w * c * batch); + + l.biases_gpu = cuda_make_array(l.biases, c); + l.bias_updates_gpu = cuda_make_array(l.bias_updates, c); - layer.output_gpu = cuda_make_array(layer.output, h * w * c * batch); - layer.delta_gpu = cuda_make_array(layer.delta, h * w * c * batch); + l.scales_gpu = cuda_make_array(l.scales, c); + l.scale_updates_gpu = cuda_make_array(l.scale_updates, c); - layer.scales_gpu = cuda_make_array(layer.scales, c); - layer.scale_updates_gpu = cuda_make_array(layer.scale_updates, c); + l.mean_gpu = cuda_make_array(l.mean, c); + l.variance_gpu = cuda_make_array(l.variance, c); - layer.mean_gpu = cuda_make_array(layer.mean, c); - layer.variance_gpu = cuda_make_array(layer.variance, c); + l.rolling_mean_gpu = cuda_make_array(l.mean, c); + l.rolling_variance_gpu = cuda_make_array(l.variance, c); - layer.rolling_mean_gpu = cuda_make_array(layer.mean, c); - layer.rolling_variance_gpu = cuda_make_array(layer.variance, c); + l.mean_delta_gpu = cuda_make_array(l.mean, c); + l.variance_delta_gpu = cuda_make_array(l.variance, c); - layer.mean_delta_gpu = cuda_make_array(layer.mean, c); - layer.variance_delta_gpu = cuda_make_array(layer.variance, c); + l.x_gpu = cuda_make_array(l.output, l.batch*l.outputs); + l.x_norm_gpu = cuda_make_array(l.output, l.batch*l.outputs); + #ifdef CUDNN + cudnnCreateTensorDescriptor(&l.normTensorDesc); + cudnnCreateTensorDescriptor(&l.dstTensorDesc); + cudnnSetTensor4dDescriptor(l.dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l.batch, l.out_c, l.out_h, l.out_w); + cudnnSetTensor4dDescriptor(l.normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l.out_c, 1, 1); - layer.x_gpu = cuda_make_array(layer.output, layer.batch*layer.outputs); - layer.x_norm_gpu = cuda_make_array(layer.output, layer.batch*layer.outputs); + #endif #endif - return layer; + return l; } void backward_scale_cpu(float *x_norm, float *delta, int batch, int n, int size, float *scale_updates) @@ -108,7 +121,7 @@ void normalize_delta_cpu(float *x, float *mean, float *variance, float *mean_del for(f = 0; f < filters; ++f){ for(k = 0; k < spatial; ++k){ int index = j*filters*spatial + f*spatial + k; - delta[index] = delta[index] * 1./(sqrt(variance[f]) + .00001f) + variance_delta[f] * 2. * (x[index] - mean[f]) / (spatial * batch) + mean_delta[f]/(spatial*batch); + delta[index] = delta[index] * 1./(sqrt(variance[f] + .00001f)) + variance_delta[f] * 2. * (x[index] - mean[f]) / (spatial * batch) + mean_delta[f]/(spatial*batch); } } } @@ -119,33 +132,35 @@ void resize_batchnorm_layer(layer *layer, int w, int h) fprintf(stderr, "Not implemented\n"); } -void forward_batchnorm_layer(layer l, network_state state) +void forward_batchnorm_layer(layer l, network net) { - if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, state.input, 1, l.output, 1); - if(l.type == CONNECTED){ - l.out_c = l.outputs; - l.out_h = l.out_w = 1; - } - if(state.train){ + if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); + copy_cpu(l.outputs*l.batch, l.output, 1, l.x, 1); + if(net.train){ mean_cpu(l.output, l.batch, l.out_c, l.out_h*l.out_w, l.mean); variance_cpu(l.output, l.mean, l.batch, l.out_c, l.out_h*l.out_w, l.variance); - scal_cpu(l.out_c, .9, l.rolling_mean, 1); - axpy_cpu(l.out_c, .1, l.mean, 1, l.rolling_mean, 1); - scal_cpu(l.out_c, .9, l.rolling_variance, 1); - axpy_cpu(l.out_c, .1, l.variance, 1, l.rolling_variance, 1); + scal_cpu(l.out_c, .99, l.rolling_mean, 1); + axpy_cpu(l.out_c, .01, l.mean, 1, l.rolling_mean, 1); + scal_cpu(l.out_c, .99, l.rolling_variance, 1); + axpy_cpu(l.out_c, .01, l.variance, 1, l.rolling_variance, 1); - copy_cpu(l.outputs*l.batch, l.output, 1, l.x, 1); normalize_cpu(l.output, l.mean, l.variance, l.batch, l.out_c, l.out_h*l.out_w); copy_cpu(l.outputs*l.batch, l.output, 1, l.x_norm, 1); } else { normalize_cpu(l.output, l.rolling_mean, l.rolling_variance, l.batch, l.out_c, l.out_h*l.out_w); } scale_bias(l.output, l.scales, l.batch, l.out_c, l.out_h*l.out_w); + add_bias(l.output, l.biases, l.batch, l.out_c, l.out_h*l.out_w); } -void backward_batchnorm_layer(const layer l, network_state state) +void backward_batchnorm_layer(layer l, network net) { + if(!net.train){ + l.mean = l.rolling_mean; + l.variance = l.rolling_variance; + } + backward_bias(l.bias_updates, l.delta, l.batch, l.out_c, l.out_w*l.out_h); backward_scale_cpu(l.x_norm, l.delta, l.batch, l.out_c, l.out_w*l.out_h, l.scale_updates); scale_bias(l.delta, l.scales, l.batch, l.out_c, l.out_h*l.out_w); @@ -153,7 +168,7 @@ void backward_batchnorm_layer(const layer l, network_state state) mean_delta_cpu(l.delta, l.variance, l.batch, l.out_c, l.out_w*l.out_h, l.mean_delta); variance_delta_cpu(l.x, l.delta, l.mean, l.variance, l.batch, l.out_c, l.out_w*l.out_h, l.variance_delta); normalize_delta_cpu(l.x, l.mean, l.variance, l.mean_delta, l.variance_delta, l.batch, l.out_c, l.out_w*l.out_h, l.delta); - if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, l.delta, 1, state.delta, 1); + if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, l.delta, 1, net.delta, 1); } #ifdef GPU @@ -171,34 +186,86 @@ void push_batchnorm_layer(layer l) cuda_push_array(l.rolling_variance_gpu, l.rolling_variance, l.c); } -void forward_batchnorm_layer_gpu(layer l, network_state state) +void forward_batchnorm_layer_gpu(layer l, network net) { - if(l.type == BATCHNORM) copy_ongpu(l.outputs*l.batch, state.input, 1, l.output_gpu, 1); - if(l.type == CONNECTED){ - l.out_c = l.outputs; - l.out_h = l.out_w = 1; - } - if (state.train) { + if(l.type == BATCHNORM) copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.x_gpu, 1); + if (net.train) { +#ifdef CUDNN + float one = 1; + float zero = 0; + cudnnBatchNormalizationForwardTraining(cudnn_handle(), + CUDNN_BATCHNORM_SPATIAL, + &one, + &zero, + l.dstTensorDesc, + l.x_gpu, + l.dstTensorDesc, + l.output_gpu, + l.normTensorDesc, + l.scales_gpu, + l.biases_gpu, + .01, + l.rolling_mean_gpu, + l.rolling_variance_gpu, + .00001, + l.mean_gpu, + l.variance_gpu); +#else fast_mean_gpu(l.output_gpu, l.batch, l.out_c, l.out_h*l.out_w, l.mean_gpu); fast_variance_gpu(l.output_gpu, l.mean_gpu, l.batch, l.out_c, l.out_h*l.out_w, l.variance_gpu); - scal_ongpu(l.out_c, .99, l.rolling_mean_gpu, 1); - axpy_ongpu(l.out_c, .01, l.mean_gpu, 1, l.rolling_mean_gpu, 1); - scal_ongpu(l.out_c, .99, l.rolling_variance_gpu, 1); - axpy_ongpu(l.out_c, .01, l.variance_gpu, 1, l.rolling_variance_gpu, 1); + scal_gpu(l.out_c, .99, l.rolling_mean_gpu, 1); + axpy_gpu(l.out_c, .01, l.mean_gpu, 1, l.rolling_mean_gpu, 1); + scal_gpu(l.out_c, .99, l.rolling_variance_gpu, 1); + axpy_gpu(l.out_c, .01, l.variance_gpu, 1, l.rolling_variance_gpu, 1); - copy_ongpu(l.outputs*l.batch, l.output_gpu, 1, l.x_gpu, 1); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.x_gpu, 1); normalize_gpu(l.output_gpu, l.mean_gpu, l.variance_gpu, l.batch, l.out_c, l.out_h*l.out_w); - copy_ongpu(l.outputs*l.batch, l.output_gpu, 1, l.x_norm_gpu, 1); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.x_norm_gpu, 1); + + scale_bias_gpu(l.output_gpu, l.scales_gpu, l.batch, l.out_c, l.out_h*l.out_w); + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.out_c, l.out_w*l.out_h); +#endif } else { normalize_gpu(l.output_gpu, l.rolling_mean_gpu, l.rolling_variance_gpu, l.batch, l.out_c, l.out_h*l.out_w); + scale_bias_gpu(l.output_gpu, l.scales_gpu, l.batch, l.out_c, l.out_h*l.out_w); + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.out_c, l.out_w*l.out_h); } - scale_bias_gpu(l.output_gpu, l.scales_gpu, l.batch, l.out_c, l.out_h*l.out_w); } -void backward_batchnorm_layer_gpu(const layer l, network_state state) +void backward_batchnorm_layer_gpu(layer l, network net) { + if(!net.train){ + l.mean_gpu = l.rolling_mean_gpu; + l.variance_gpu = l.rolling_variance_gpu; + } +#ifdef CUDNN + float one = 1; + float zero = 0; + cudnnBatchNormalizationBackward(cudnn_handle(), + CUDNN_BATCHNORM_SPATIAL, + &one, + &zero, + &one, + &one, + l.dstTensorDesc, + l.x_gpu, + l.dstTensorDesc, + l.delta_gpu, + l.dstTensorDesc, + l.x_norm_gpu, + l.normTensorDesc, + l.scales_gpu, + l.scale_updates_gpu, + l.bias_updates_gpu, + .00001, + l.mean_gpu, + l.variance_gpu); + copy_gpu(l.outputs*l.batch, l.x_norm_gpu, 1, l.delta_gpu, 1); +#else + backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.out_c, l.out_w*l.out_h); backward_scale_gpu(l.x_norm_gpu, l.delta_gpu, l.batch, l.out_c, l.out_w*l.out_h, l.scale_updates_gpu); scale_bias_gpu(l.delta_gpu, l.scales_gpu, l.batch, l.out_c, l.out_h*l.out_w); @@ -206,6 +273,7 @@ void backward_batchnorm_layer_gpu(const layer l, network_state state) fast_mean_delta_gpu(l.delta_gpu, l.variance_gpu, l.batch, l.out_c, l.out_w*l.out_h, l.mean_delta_gpu); fast_variance_delta_gpu(l.x_gpu, l.delta_gpu, l.mean_gpu, l.variance_gpu, l.batch, l.out_c, l.out_w*l.out_h, l.variance_delta_gpu); normalize_delta_gpu(l.x_gpu, l.mean_gpu, l.variance_gpu, l.mean_delta_gpu, l.variance_delta_gpu, l.batch, l.out_c, l.out_w*l.out_h, l.delta_gpu); - if(l.type == BATCHNORM) copy_ongpu(l.outputs*l.batch, l.delta_gpu, 1, state.delta, 1); +#endif + if(l.type == BATCHNORM) copy_gpu(l.outputs*l.batch, l.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/src/batchnorm_layer.h b/image.darknet/src/batchnorm_layer.h index 99d1d0f..25a18a3 100644 --- a/image.darknet/src/batchnorm_layer.h +++ b/image.darknet/src/batchnorm_layer.h @@ -6,12 +6,12 @@ #include "network.h" layer make_batchnorm_layer(int batch, int w, int h, int c); -void forward_batchnorm_layer(layer l, network_state state); -void backward_batchnorm_layer(layer l, network_state state); +void forward_batchnorm_layer(layer l, network net); +void backward_batchnorm_layer(layer l, network net); #ifdef GPU -void forward_batchnorm_layer_gpu(layer l, network_state state); -void backward_batchnorm_layer_gpu(layer l, network_state state); +void forward_batchnorm_layer_gpu(layer l, network net); +void backward_batchnorm_layer_gpu(layer l, network net); void pull_batchnorm_layer(layer l); void push_batchnorm_layer(layer l); #endif diff --git a/image.darknet/src/blas.c b/image.darknet/src/blas.c index 31bd86b..9e16044 100644 --- a/image.darknet/src/blas.c +++ b/image.darknet/src/blas.c @@ -1,5 +1,6 @@ #include "blas.h" -#include "math.h" + +#include #include #include #include @@ -54,7 +55,17 @@ void weighted_sum_cpu(float *a, float *b, float *s, int n, float *c) } } -void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out) +void weighted_delta_cpu(float *a, float *b, float *s, float *da, float *db, float *ds, int n, float *dc) +{ + int i; + for(i = 0; i < n; ++i){ + if(da) da[i] += dc[i] * s[i]; + if(db) db[i] += dc[i] * (1-s[i]); + ds[i] += dc[i] * (a[i] - b[i]); + } +} + +void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out) { int stride = w1/w2; int sample = w2/w1; @@ -73,7 +84,7 @@ void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, for(i = 0; i < minw; ++i){ int out_index = i*sample + w2*(j*sample + h2*(k + c2*b)); int add_index = i*stride + w1*(j*stride + h1*(k + c1*b)); - out[out_index] += add[add_index]; + out[out_index] = s1*out[out_index] + s2*add[add_index]; } } } @@ -112,6 +123,27 @@ void variance_cpu(float *x, float *mean, int batch, int filters, int spatial, fl } } +void l2normalize_cpu(float *x, float *dx, int batch, int filters, int spatial) +{ + int b,f,i; + for(b = 0; b < batch; ++b){ + for(i = 0; i < spatial; ++i){ + float sum = 0; + for(f = 0; f < filters; ++f){ + int index = b*filters*spatial + f*spatial + i; + sum += powf(x[index], 2); + } + sum = sqrtf(sum); + for(f = 0; f < filters; ++f){ + int index = b*filters*spatial + f*spatial + i; + x[index] /= sum; + dx[index] = (1 - x[index]) / sum; + } + } + } +} + + void normalize_cpu(float *x, float *mean, float *variance, int batch, int filters, int spatial) { int b, f, i; @@ -161,12 +193,48 @@ void fill_cpu(int N, float ALPHA, float *X, int INCX) for(i = 0; i < N; ++i) X[i*INCX] = ALPHA; } +void deinter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + int i, j; + int index = 0; + for(j = 0; j < B; ++j) { + for(i = 0; i < NX; ++i){ + if(X) X[j*NX + i] += OUT[index]; + ++index; + } + for(i = 0; i < NY; ++i){ + if(Y) Y[j*NY + i] += OUT[index]; + ++index; + } + } +} + +void inter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + int i, j; + int index = 0; + for(j = 0; j < B; ++j) { + for(i = 0; i < NX; ++i){ + OUT[index++] = X[j*NX + i]; + } + for(i = 0; i < NY; ++i){ + OUT[index++] = Y[j*NY + i]; + } + } +} + void copy_cpu(int N, float *X, int INCX, float *Y, int INCY) { int i; for(i = 0; i < N; ++i) Y[i*INCY] = X[i*INCX]; } +void mult_add_into_cpu(int N, float *X, float *Y, float *Z) +{ + int i; + for(i = 0; i < N; ++i) Z[i] += X[i]*Y[i]; +} + void smooth_l1_cpu(int n, float *pred, float *truth, float *delta, float *error) { int i; @@ -179,11 +247,43 @@ void smooth_l1_cpu(int n, float *pred, float *truth, float *delta, float *error) } else { error[i] = 2*abs_val - 1; - delta[i] = (diff < 0) ? -1 : 1; + delta[i] = (diff < 0) ? 1 : -1; } } } +void l1_cpu(int n, float *pred, float *truth, float *delta, float *error) +{ + int i; + for(i = 0; i < n; ++i){ + float diff = truth[i] - pred[i]; + error[i] = fabs(diff); + delta[i] = diff > 0 ? 1 : -1; + } +} + +void softmax_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error) +{ + int i; + for(i = 0; i < n; ++i){ + float t = truth[i]; + float p = pred[i]; + error[i] = (t) ? -log(p) : 0; + delta[i] = t-p; + } +} + +void logistic_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error) +{ + int i; + for(i = 0; i < n; ++i){ + float t = truth[i]; + float p = pred[i]; + error[i] = -t*log(p) - (1-t)*log(1-p); + delta[i] = t-p; + } +} + void l2_cpu(int n, float *pred, float *truth, float *delta, float *error) { int i; @@ -202,21 +302,50 @@ float dot_cpu(int N, float *X, int INCX, float *Y, int INCY) return dot; } -void softmax(float *input, int n, float temp, float *output) +void softmax(float *input, int n, float temp, int stride, float *output) { int i; float sum = 0; float largest = -FLT_MAX; for(i = 0; i < n; ++i){ - if(input[i] > largest) largest = input[i]; + if(input[i*stride] > largest) largest = input[i*stride]; } for(i = 0; i < n; ++i){ - float e = exp(input[i]/temp - largest/temp); + float e = exp(input[i*stride]/temp - largest/temp); sum += e; - output[i] = e; + output[i*stride] = e; } for(i = 0; i < n; ++i){ - output[i] /= sum; + output[i*stride] /= sum; } } + +void softmax_cpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output) +{ + int g, b; + for(b = 0; b < batch; ++b){ + for(g = 0; g < groups; ++g){ + softmax(input + b*batch_offset + g*group_offset, n, temp, stride, output + b*batch_offset + g*group_offset); + } + } +} + +void upsample_cpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out) +{ + int i, j, k, b; + for(b = 0; b < batch; ++b){ + for(k = 0; k < c; ++k){ + for(j = 0; j < h*stride; ++j){ + for(i = 0; i < w*stride; ++i){ + int in_index = b*w*h*c + k*w*h + (j/stride)*w + i/stride; + int out_index = b*w*h*c*stride*stride + k*w*h*stride*stride + j*w*stride + i; + if(forward) out[out_index] = scale*in[in_index]; + else in[in_index] += scale*out[out_index]; + } + } + } + } +} + + diff --git a/image.darknet/src/blas.h b/image.darknet/src/blas.h index 3d6ee7d..707291d 100644 --- a/image.darknet/src/blas.h +++ b/image.darknet/src/blas.h @@ -1,5 +1,7 @@ #ifndef BLAS_H #define BLAS_H +#include "darknet.h" + void flatten(float *x, int size, int layers, int batch, int forward); void pm(int M, int N, float *A); float *random_matrix(int rows, int cols); @@ -8,53 +10,60 @@ void reorg_cpu(float *x, int w, int h, int c, int batch, int stride, int forward void test_blas(); +void inter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT); +void deinter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT); +void mult_add_into_cpu(int N, float *X, float *Y, float *Z); + void const_cpu(int N, float ALPHA, float *X, int INCX); -void constrain_ongpu(int N, float ALPHA, float * X, int INCX); +void constrain_gpu(int N, float ALPHA, float * X, int INCX); void pow_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); void mul_cpu(int N, float *X, int INCX, float *Y, int INCY); -void axpy_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); -void copy_cpu(int N, float *X, int INCX, float *Y, int INCY); -void scal_cpu(int N, float ALPHA, float *X, int INCX); -void fill_cpu(int N, float ALPHA, float * X, int INCX); -float dot_cpu(int N, float *X, int INCX, float *Y, int INCY); -void test_gpu_blas(); -void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out); +int test_gpu_blas(); +void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out); void mean_cpu(float *x, int batch, int filters, int spatial, float *mean); void variance_cpu(float *x, float *mean, int batch, int filters, int spatial, float *variance); -void normalize_cpu(float *x, float *mean, float *variance, int batch, int filters, int spatial); void scale_bias(float *output, float *scales, int batch, int n, int size); void backward_scale_cpu(float *x_norm, float *delta, int batch, int n, int size, float *scale_updates); void mean_delta_cpu(float *delta, float *variance, int batch, int filters, int spatial, float *mean_delta); void variance_delta_cpu(float *x, float *delta, float *mean, float *variance, int batch, int filters, int spatial, float *variance_delta); void normalize_delta_cpu(float *x, float *mean, float *variance, float *mean_delta, float *variance_delta, int batch, int filters, int spatial, float *delta); +void l2normalize_cpu(float *x, float *dx, int batch, int filters, int spatial); void smooth_l1_cpu(int n, float *pred, float *truth, float *delta, float *error); void l2_cpu(int n, float *pred, float *truth, float *delta, float *error); +void l1_cpu(int n, float *pred, float *truth, float *delta, float *error); +void logistic_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error); +void softmax_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error); void weighted_sum_cpu(float *a, float *b, float *s, int num, float *c); +void weighted_delta_cpu(float *a, float *b, float *s, float *da, float *db, float *ds, int n, float *dc); -void softmax(float *input, int n, float temp, float *output); +void softmax(float *input, int n, float temp, int stride, float *output); +void softmax_cpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output); +void upsample_cpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out); #ifdef GPU #include "cuda.h" - -void axpy_ongpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY); -void axpy_ongpu_offset(int N, float ALPHA, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY); -void copy_ongpu(int N, float * X, int INCX, float * Y, int INCY); -void copy_ongpu_offset(int N, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY); -void scal_ongpu(int N, float ALPHA, float * X, int INCX); -void supp_ongpu(int N, float ALPHA, float * X, int INCX); -void mask_ongpu(int N, float * X, float mask_num, float * mask); -void const_ongpu(int N, float ALPHA, float *X, int INCX); -void pow_ongpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); -void mul_ongpu(int N, float *X, int INCX, float *Y, int INCY); -void fill_ongpu(int N, float ALPHA, float * X, int INCX); +#include "tree.h" + +void axpy_gpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY); +void axpy_gpu_offset(int N, float ALPHA, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY); +void copy_gpu(int N, float * X, int INCX, float * Y, int INCY); +void copy_gpu_offset(int N, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY); +void add_gpu(int N, float ALPHA, float * X, int INCX); +void supp_gpu(int N, float ALPHA, float * X, int INCX); +void mask_gpu(int N, float * X, float mask_num, float * mask, float val); +void scale_mask_gpu(int N, float * X, float mask_num, float * mask, float scale); +void const_gpu(int N, float ALPHA, float *X, int INCX); +void pow_gpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); +void mul_gpu(int N, float *X, int INCX, float *Y, int INCY); void mean_gpu(float *x, int batch, int filters, int spatial, float *mean); void variance_gpu(float *x, float *mean, int batch, int filters, int spatial, float *variance); void normalize_gpu(float *x, float *mean, float *variance, int batch, int filters, int spatial); +void l2normalize_gpu(float *x, float *dx, int batch, int filters, int spatial); void normalize_delta_gpu(float *x, float *mean, float *variance, float *mean_delta, float *variance_delta, int batch, int filters, int spatial, float *delta); @@ -63,25 +72,34 @@ void fast_variance_delta_gpu(float *x, float *delta, float *mean, float *varianc void fast_variance_gpu(float *x, float *mean, int batch, int filters, int spatial, float *variance); void fast_mean_gpu(float *x, int batch, int filters, int spatial, float *mean); -void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out); +void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out); void scale_bias_gpu(float *output, float *biases, int batch, int n, int size); void backward_scale_gpu(float *x_norm, float *delta, int batch, int n, int size, float *scale_updates); void scale_bias_gpu(float *output, float *biases, int batch, int n, int size); void add_bias_gpu(float *output, float *biases, int batch, int n, int size); void backward_bias_gpu(float *bias_updates, float *delta, int batch, int n, int size); +void logistic_x_ent_gpu(int n, float *pred, float *truth, float *delta, float *error); +void softmax_x_ent_gpu(int n, float *pred, float *truth, float *delta, float *error); void smooth_l1_gpu(int n, float *pred, float *truth, float *delta, float *error); void l2_gpu(int n, float *pred, float *truth, float *delta, float *error); +void l1_gpu(int n, float *pred, float *truth, float *delta, float *error); +void wgan_gpu(int n, float *pred, float *truth, float *delta, float *error); void weighted_delta_gpu(float *a, float *b, float *s, float *da, float *db, float *ds, int num, float *dc); void weighted_sum_gpu(float *a, float *b, float *s, int num, float *c); void mult_add_into_gpu(int num, float *a, float *b, float *c); +void inter_gpu(int NX, float *X, int NY, float *Y, int B, float *OUT); +void deinter_gpu(int NX, float *X, int NY, float *Y, int B, float *OUT); -void reorg_ongpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out); +void reorg_gpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out); -void softmax_gpu(float *input, int n, int offset, int groups, float temp, float *output); +void softmax_gpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output); +void adam_update_gpu(float *w, float *d, float *m, float *v, float B1, float B2, float eps, float decay, float rate, int n, int batch, int t); void adam_gpu(int n, float *x, float *m, float *v, float B1, float B2, float rate, float eps, int t); -void flatten_ongpu(float *x, int spatial, int layers, int batch, int forward, float *out); +void flatten_gpu(float *x, int spatial, int layers, int batch, int forward, float *out); +void softmax_tree(float *input, int spatial, int batch, int stride, float temp, float *output, tree hier); +void upsample_gpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out); #endif #endif diff --git a/image.darknet/src/blas_kernels.cu b/image.darknet/src/blas_kernels.cu index d940176..47e8217 100644 --- a/image.darknet/src/blas_kernels.cu +++ b/image.darknet/src/blas_kernels.cu @@ -53,24 +53,40 @@ void backward_scale_gpu(float *x_norm, float *delta, int batch, int n, int size, check_error(cudaPeekAtLastError()); } -__global__ void add_bias_kernel(float *output, float *biases, int n, int size) +__global__ void add_bias_kernel(float *output, float *biases, int batch, int n, int size) { - int offset = blockIdx.x * blockDim.x + threadIdx.x; - int filter = blockIdx.y; - int batch = blockIdx.z; + int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (index >= n*size*batch) return; + int i = index % size; + index /= size; + int j = index % n; + index /= n; + int k = index; - if(offset < size) output[(batch*n+filter)*size + offset] += biases[filter]; + output[(k*n+j)*size + i] += biases[j]; } void add_bias_gpu(float *output, float *biases, int batch, int n, int size) { - dim3 dimGrid((size-1)/BLOCK + 1, n, batch); - dim3 dimBlock(BLOCK, 1, 1); + int num = n*size*batch; - add_bias_kernel<<>>(output, biases, n, size); + add_bias_kernel<<>>(output, biases, batch, n, size); check_error(cudaPeekAtLastError()); } +__global__ void backward_bias_conn_kernel(float *bias_updates, float *delta, int batch, int n) +{ + int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (index >= n) return; + int b; + float sum = 0; + for(b = 0; b < batch; ++b){ + int i = b*n + index; + sum += delta[i]; + } + bias_updates[index] += sum; +} + __global__ void backward_bias_kernel(float *bias_updates, float *delta, int batch, int n, int size) { __shared__ float part[BLOCK]; @@ -91,6 +107,16 @@ __global__ void backward_bias_kernel(float *bias_updates, float *delta, int batc } } +void backward_bias_gpu(float *bias_updates, float *delta, int batch, int n, int size) +{ + if(size == 1){ + backward_bias_conn_kernel<<>>(bias_updates, delta, batch, n); + }else{ + backward_bias_kernel<<>>(bias_updates, delta, batch, n, size); + } + check_error(cudaPeekAtLastError()); +} + /* __global__ void dot_kernel(float *output, float scale, int batch, int n, int size, float *delta) { @@ -133,20 +159,16 @@ void dot_error_gpu(layer l) } */ -void backward_bias_gpu(float *bias_updates, float *delta, int batch, int n, int size) -{ - backward_bias_kernel<<>>(bias_updates, delta, batch, n, size); - check_error(cudaPeekAtLastError()); -} - __global__ void adam_kernel(int N, float *x, float *m, float *v, float B1, float B2, float rate, float eps, int t) { int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (index >= N) return; + + float mhat = m[index] / (1.f - powf(B1, t)); + float vhat = v[index] / (1.f - powf(B2, t)); - x[index] = x[index] - (rate * sqrt(1.-pow(B2, t)) / (1.-pow(B1, t)) * m[index] / (sqrt(v[index]) + eps)); - //if(index == 0) printf("%f %f %f %f\n", m[index], v[index], (rate * sqrt(1.-pow(B2, t)) / (1.-pow(B1, t)) * m[index] / (sqrt(v[index]) + eps))); + x[index] = x[index] + rate * mhat / (sqrtf(vhat) + eps); } extern "C" void adam_gpu(int n, float *x, float *m, float *v, float B1, float B2, float rate, float eps, int t) @@ -155,13 +177,27 @@ extern "C" void adam_gpu(int n, float *x, float *m, float *v, float B1, float B2 check_error(cudaPeekAtLastError()); } +extern "C" void adam_update_gpu(float *w, float *d, float *m, float *v, float B1, float B2, float eps, float decay, float rate, int n, int batch, int t) +{ + scal_gpu(n, B1, m, 1); + scal_gpu(n, B2, v, 1); + axpy_gpu(n, -decay*batch, w, 1, d, 1); + + axpy_gpu(n, (1-B1), d, 1, m, 1); + mul_gpu(n, d, 1, d, 1); + axpy_gpu(n, (1-B2), d, 1, v, 1); + + adam_gpu(n, w, m, v, B1, B2, rate, eps, t); + fill_gpu(n, 0, d, 1); +} + __global__ void normalize_kernel(int N, float *x, float *mean, float *variance, int batch, int filters, int spatial) { int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (index >= N) return; int f = (index/spatial)%filters; - x[index] = (x[index] - mean[f])/(sqrt(variance[f]) + .000001f); + x[index] = (x[index] - mean[f])/(sqrtf(variance[f] + .00001f)); } __global__ void normalize_delta_kernel(int N, float *x, float *mean, float *variance, float *mean_delta, float *variance_delta, int batch, int filters, int spatial, float *delta) @@ -170,7 +206,7 @@ __global__ void normalize_delta_kernel(int N, float *x, float *mean, float *vari if (index >= N) return; int f = (index/spatial)%filters; - delta[index] = delta[index] * 1./(sqrt(variance[f]) + .000001f) + variance_delta[f] * 2. * (x[index] - mean[f]) / (spatial * batch) + mean_delta[f]/(spatial*batch); + delta[index] = delta[index] * 1.f/(sqrtf(variance[f] + .00001f)) + variance_delta[f] * 2.f * (x[index] - mean[f]) / (spatial * batch) + mean_delta[f]/(spatial*batch); } extern "C" void normalize_delta_gpu(float *x, float *mean, float *variance, float *mean_delta, float *variance_delta, int batch, int filters, int spatial, float *delta) @@ -192,7 +228,7 @@ __global__ void variance_delta_kernel(float *x, float *delta, float *mean, floa variance_delta[i] += delta[index]*(x[index] - mean[i]); } } - variance_delta[i] *= -.5 * pow(variance[i] + .000001f, (float)(-3./2.)); + variance_delta[i] *= -.5f * powf(variance[i] + .00001f, (float)(-3.f/2.f)); } __global__ void accumulate_kernel(float *x, int n, int groups, float *sum) @@ -224,12 +260,14 @@ __global__ void fast_mean_delta_kernel(float *delta, float *variance, int batch, } } + __syncthreads(); + if(id == 0){ mean_delta[filter] = 0; for(i = 0; i < threads; ++i){ mean_delta[filter] += local[i]; } - mean_delta[filter] *= (-1./sqrt(variance[filter] + .000001f)); + mean_delta[filter] *= (-1.f/sqrtf(variance[filter] + .00001f)); } } @@ -252,12 +290,14 @@ __global__ void fast_variance_delta_kernel(float *x, float *delta, float *mean, } } + __syncthreads(); + if(id == 0){ variance_delta[filter] = 0; for(i = 0; i < threads; ++i){ variance_delta[filter] += local[i]; } - variance_delta[filter] *= -.5 * pow(variance[filter] + .000001f, (float)(-3./2.)); + variance_delta[filter] *= -.5f * powf(variance[filter] + .00001f, (float)(-3.f/2.f)); } } @@ -274,7 +314,7 @@ __global__ void mean_delta_kernel(float *delta, float *variance, int batch, int mean_delta[i] += delta[index]; } } - mean_delta[i] *= (-1./sqrt(variance[i] + .000001f)); + mean_delta[i] *= (-1.f/sqrtf(variance[i] + .00001f)); } extern "C" void mean_delta_gpu(float *delta, float *variance, int batch, int filters, int spatial, float *mean_delta) @@ -297,7 +337,7 @@ extern "C" void fast_variance_delta_gpu(float *x, float *delta, float *mean, flo __global__ void mean_kernel(float *x, int batch, int filters, int spatial, float *mean) { - float scale = 1./(batch * spatial); + float scale = 1.f/(batch * spatial); int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (i >= filters) return; int j,k; @@ -313,7 +353,7 @@ __global__ void mean_kernel(float *x, int batch, int filters, int spatial, floa __global__ void variance_kernel(float *x, float *mean, int batch, int filters, int spatial, float *variance) { - float scale = 1./(batch * spatial - 1); + float scale = 1.f/(batch * spatial - 1); int j,k; int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (i >= filters) return; @@ -321,7 +361,7 @@ __global__ void variance_kernel(float *x, float *mean, int batch, int filters, i for(j = 0; j < batch; ++j){ for(k = 0; k < spatial; ++k){ int index = j*filters*spatial + i*spatial + k; - variance[i] += pow((x[index] - mean[i]), 2); + variance[i] += powf((x[index] - mean[i]), 2); } } variance[i] *= scale; @@ -391,22 +431,22 @@ __global__ void supp_kernel(int N, float ALPHA, float *X, int INCX) } } -__global__ void scal_kernel(int N, float ALPHA, float *X, int INCX) +__global__ void add_kernel(int N, float ALPHA, float *X, int INCX) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; - if(i < N) X[i*INCX] *= ALPHA; + if(i < N) X[i*INCX] += ALPHA; } -__global__ void fill_kernel(int N, float ALPHA, float *X, int INCX) +__global__ void scal_kernel(int N, float ALPHA, float *X, int INCX) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; - if(i < N) X[i*INCX] = ALPHA; + if(i < N) X[i*INCX] *= ALPHA; } -__global__ void mask_kernel(int n, float *x, float mask_num, float *mask) +__global__ void fill_kernel(int N, float ALPHA, float *X, int INCX) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; - if(i < n && mask[i] == mask_num) x[i] = mask_num; + if(i < N) X[i*INCX] = ALPHA; } __global__ void copy_kernel(int N, float *X, int OFFX, int INCX, float *Y, int OFFY, int INCY) @@ -429,6 +469,35 @@ extern "C" void normalize_gpu(float *x, float *mean, float *variance, int batch, check_error(cudaPeekAtLastError()); } +__global__ void l2norm_kernel(int N, float *x, float *dx, int batch, int filters, int spatial) +{ + int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (index >= N) return; + int b = index / spatial; + int i = index % spatial; + int f; + float sum = 0; + for(f = 0; f < filters; ++f){ + int index = b*filters*spatial + f*spatial + i; + sum += powf(x[index], 2); + } + sum = sqrtf(sum); + if(sum == 0) sum = 1; + //printf("%f\n", sum); + for(f = 0; f < filters; ++f){ + int index = b*filters*spatial + f*spatial + i; + x[index] /= sum; + dx[index] = (1 - x[index]) / sum; + } +} + +extern "C" void l2normalize_gpu(float *x, float *dx, int batch, int filters, int spatial) +{ + size_t N = batch*spatial; + l2norm_kernel<<>>(N, x, dx, batch, filters, spatial); + check_error(cudaPeekAtLastError()); +} + __global__ void fast_mean_kernel(float *x, int batch, int filters, int spatial, float *mean) { const int threads = BLOCK; @@ -447,6 +516,8 @@ __global__ void fast_mean_kernel(float *x, int batch, int filters, int spatial, } } + __syncthreads(); + if(id == 0){ mean[filter] = 0; for(i = 0; i < threads; ++i){ @@ -471,10 +542,12 @@ __global__ void fast_variance_kernel(float *x, float *mean, int batch, int filt for(i = 0; i < spatial; i += threads){ int index = j*spatial*filters + filter*spatial + i + id; - local[id] += (i+id < spatial) ? pow((x[index] - mean[filter]), 2) : 0; + local[id] += (i+id < spatial) ? powf((x[index] - mean[filter]), 2) : 0; } } + __syncthreads(); + if(id == 0){ variance[filter] = 0; for(i = 0; i < threads; ++i){ @@ -509,35 +582,35 @@ extern "C" void variance_gpu(float *x, float *mean, int batch, int filters, int check_error(cudaPeekAtLastError()); } -extern "C" void axpy_ongpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY) +extern "C" void axpy_gpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY) { - axpy_ongpu_offset(N, ALPHA, X, 0, INCX, Y, 0, INCY); + axpy_gpu_offset(N, ALPHA, X, 0, INCX, Y, 0, INCY); } -extern "C" void pow_ongpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY) +extern "C" void pow_gpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY) { pow_kernel<<>>(N, ALPHA, X, INCX, Y, INCY); check_error(cudaPeekAtLastError()); } -extern "C" void axpy_ongpu_offset(int N, float ALPHA, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY) +extern "C" void axpy_gpu_offset(int N, float ALPHA, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY) { axpy_kernel<<>>(N, ALPHA, X, OFFX, INCX, Y, OFFY, INCY); check_error(cudaPeekAtLastError()); } -extern "C" void copy_ongpu(int N, float * X, int INCX, float * Y, int INCY) +extern "C" void copy_gpu(int N, float * X, int INCX, float * Y, int INCY) { - copy_ongpu_offset(N, X, 0, INCX, Y, 0, INCY); + copy_gpu_offset(N, X, 0, INCX, Y, 0, INCY); } -extern "C" void mul_ongpu(int N, float * X, int INCX, float * Y, int INCY) +extern "C" void mul_gpu(int N, float * X, int INCX, float * Y, int INCY) { mul_kernel<<>>(N, X, INCX, Y, INCY); check_error(cudaPeekAtLastError()); } -extern "C" void copy_ongpu_offset(int N, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY) +extern "C" void copy_gpu_offset(int N, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY) { copy_kernel<<>>(N, X, OFFX, INCX, Y, OFFY, INCY); check_error(cudaPeekAtLastError()); @@ -560,58 +633,82 @@ __global__ void flatten_kernel(int N, float *x, int spatial, int layers, int bat else out[i1] = x[i2]; } -extern "C" void flatten_ongpu(float *x, int spatial, int layers, int batch, int forward, float *out) +extern "C" void flatten_gpu(float *x, int spatial, int layers, int batch, int forward, float *out) { int size = spatial*batch*layers; flatten_kernel<<>>(size, x, spatial, layers, batch, forward, out); check_error(cudaPeekAtLastError()); } -extern "C" void reorg_ongpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out) +extern "C" void reorg_gpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out) { int size = w*h*c*batch; reorg_kernel<<>>(size, x, w, h, c, batch, stride, forward, out); check_error(cudaPeekAtLastError()); } -extern "C" void mask_ongpu(int N, float * X, float mask_num, float * mask) +__global__ void mask_kernel(int n, float *x, float mask_num, float *mask, float val) { - mask_kernel<<>>(N, X, mask_num, mask); + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n && mask[i] == mask_num) x[i] = val; +} + +extern "C" void mask_gpu(int N, float * X, float mask_num, float * mask, float val) +{ + mask_kernel<<>>(N, X, mask_num, mask, val); + check_error(cudaPeekAtLastError()); +} + +__global__ void scale_mask_kernel(int n, float *x, float mask_num, float *mask, float scale) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n && mask[i] == mask_num) x[i] *= scale; +} + +extern "C" void scale_mask_gpu(int N, float * X, float mask_num, float * mask, float scale) +{ + scale_mask_kernel<<>>(N, X, mask_num, mask, scale); check_error(cudaPeekAtLastError()); } -extern "C" void const_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void const_gpu(int N, float ALPHA, float * X, int INCX) { const_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -extern "C" void constrain_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void constrain_gpu(int N, float ALPHA, float * X, int INCX) { constrain_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -extern "C" void scal_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void add_gpu(int N, float ALPHA, float * X, int INCX) +{ + add_kernel<<>>(N, ALPHA, X, INCX); + check_error(cudaPeekAtLastError()); +} + +extern "C" void scal_gpu(int N, float ALPHA, float * X, int INCX) { scal_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -extern "C" void supp_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void supp_gpu(int N, float ALPHA, float * X, int INCX) { supp_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -extern "C" void fill_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void fill_gpu(int N, float ALPHA, float * X, int INCX) { fill_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -__global__ void shortcut_kernel(int size, int minw, int minh, int minc, int stride, int sample, int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out) +__global__ void shortcut_kernel(int size, int minw, int minh, int minc, int stride, int sample, int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out) { int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (id >= size) return; @@ -625,10 +722,11 @@ __global__ void shortcut_kernel(int size, int minw, int minh, int minc, int stri int out_index = i*sample + w2*(j*sample + h2*(k + c2*b)); int add_index = i*stride + w1*(j*stride + h1*(k + c1*b)); - out[out_index] += add[add_index]; + out[out_index] = s1*out[out_index] + s2*add[add_index]; + //out[out_index] += add[add_index]; } -extern "C" void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out) +extern "C" void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out) { int minw = (w1 < w2) ? w1 : w2; int minh = (h1 < h2) ? h1 : h2; @@ -642,7 +740,7 @@ extern "C" void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int if(sample < 1) sample = 1; int size = batch * minw * minh * minc; - shortcut_kernel<<>>(size, minw, minh, minc, stride, sample, batch, w1, h1, c1, add, w2, h2, c2, out); + shortcut_kernel<<>>(size, minw, minh, minc, stride, sample, batch, w1, h1, c1, add, w2, h2, c2, s1, s2, out); check_error(cudaPeekAtLastError()); } @@ -651,14 +749,14 @@ __global__ void smooth_l1_kernel(int n, float *pred, float *truth, float *delta, int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if(i < n){ float diff = truth[i] - pred[i]; - float abs_val = abs(diff); + float abs_val = fabsf(diff); if(abs_val < 1) { error[i] = diff * diff; delta[i] = diff; } else { error[i] = 2*abs_val - 1; - delta[i] = (diff < 0) ? -1 : 1; + delta[i] = (diff > 0) ? 1 : -1; } } } @@ -669,6 +767,40 @@ extern "C" void smooth_l1_gpu(int n, float *pred, float *truth, float *delta, fl check_error(cudaPeekAtLastError()); } +__global__ void softmax_x_ent_kernel(int n, float *pred, float *truth, float *delta, float *error) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n){ + float t = truth[i]; + float p = pred[i]; + error[i] = (t) ? -log(p) : 0; + delta[i] = t-p; + } +} + +extern "C" void softmax_x_ent_gpu(int n, float *pred, float *truth, float *delta, float *error) +{ + softmax_x_ent_kernel<<>>(n, pred, truth, delta, error); + check_error(cudaPeekAtLastError()); +} + +__global__ void logistic_x_ent_kernel(int n, float *pred, float *truth, float *delta, float *error) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n){ + float t = truth[i]; + float p = pred[i]; + error[i] = -t*log(p+.0000001) - (1-t)*log(1-p+.0000001); + delta[i] = t-p; + } +} + +extern "C" void logistic_x_ent_gpu(int n, float *pred, float *truth, float *delta, float *error) +{ + logistic_x_ent_kernel<<>>(n, pred, truth, delta, error); + check_error(cudaPeekAtLastError()); +} + __global__ void l2_kernel(int n, float *pred, float *truth, float *delta, float *error) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; @@ -685,6 +817,38 @@ extern "C" void l2_gpu(int n, float *pred, float *truth, float *delta, float *er check_error(cudaPeekAtLastError()); } +__global__ void l1_kernel(int n, float *pred, float *truth, float *delta, float *error) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n){ + float diff = truth[i] - pred[i]; + error[i] = abs(diff); + delta[i] = (diff > 0) ? 1 : -1; + } +} + +extern "C" void l1_gpu(int n, float *pred, float *truth, float *delta, float *error) +{ + l1_kernel<<>>(n, pred, truth, delta, error); + check_error(cudaPeekAtLastError()); +} + +__global__ void wgan_kernel(int n, float *pred, float *truth, float *delta, float *error) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n){ + error[i] = truth[i] ? -pred[i] : pred[i]; + delta[i] = (truth[i] > 0) ? 1 : -1; + } +} + +extern "C" void wgan_gpu(int n, float *pred, float *truth, float *delta, float *error) +{ + wgan_kernel<<>>(n, pred, truth, delta, error); + check_error(cudaPeekAtLastError()); +} + + __global__ void weighted_sum_kernel(int n, float *a, float *b, float *s, float *c) @@ -695,6 +859,46 @@ __global__ void weighted_sum_kernel(int n, float *a, float *b, float *s, float * } } +__global__ void deinter_kernel(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < (NX+NY)*B){ + int b = i / (NX+NY); + int j = i % (NX+NY); + if (j < NX){ + if(X) X[b*NX + j] += OUT[i]; + } else { + if(Y) Y[b*NY + j - NX] += OUT[i]; + } + } +} + +extern "C" void deinter_gpu(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + deinter_kernel<<>>(NX, X, NY, Y, B, OUT); + check_error(cudaPeekAtLastError()); +} + +__global__ void inter_kernel(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < (NX+NY)*B){ + int b = i / (NX+NY); + int j = i % (NX+NY); + if (j < NX){ + OUT[i] = X[b*NX + j]; + } else { + OUT[i] = Y[b*NY + j - NX]; + } + } +} + +extern "C" void inter_gpu(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + inter_kernel<<>>(NX, X, NY, Y, B, OUT); + check_error(cudaPeekAtLastError()); +} + extern "C" void weighted_sum_gpu(float *a, float *b, float *s, int num, float *c) { weighted_sum_kernel<<>>(num, a, b, s, c); @@ -706,8 +910,8 @@ __global__ void weighted_delta_kernel(int n, float *a, float *b, float *s, float int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if(i < n){ if(da) da[i] += dc[i] * s[i]; - db[i] += dc[i] * (1-s[i]); - ds[i] += dc[i] * a[i] + dc[i] * -b[i]; + if(db) db[i] += dc[i] * (1-s[i]); + ds[i] += dc[i] * (a[i] - b[i]); } } @@ -732,36 +936,100 @@ extern "C" void mult_add_into_gpu(int num, float *a, float *b, float *c) } -__device__ void softmax_device(int n, float *input, float temp, float *output) +__device__ void softmax_device(float *input, int n, float temp, int stride, float *output) { int i; float sum = 0; float largest = -INFINITY; for(i = 0; i < n; ++i){ - int val = input[i]; + int val = input[i*stride]; largest = (val>largest) ? val : largest; } for(i = 0; i < n; ++i){ - float e = exp(input[i]/temp - largest/temp); + float e = expf(input[i*stride]/temp - largest/temp); sum += e; - output[i] = e; + output[i*stride] = e; } for(i = 0; i < n; ++i){ - output[i] /= sum; + output[i*stride] /= sum; } } -__global__ void softmax_kernel(int n, int offset, int batch, float *input, float temp, float *output) + +__global__ void softmax_tree_kernel(float *input, int spatial, int batch, int stride, float temp, float *output, int groups, int *group_size, int *group_offset) { - int b = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; - if(b >= batch) return; - softmax_device(n, input + b*offset, temp, output + b*offset); + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (id >= spatial*batch*groups) return; + int s = id % spatial; + id = id / spatial; + int g = id % groups; + int b = id / groups; + int goff = group_offset[g]*spatial; + int boff = b*stride; + softmax_device(input + goff + boff + s, group_size[g], temp, spatial, output + goff + boff + s); +} + +extern "C" void softmax_tree(float *input, int spatial, int batch, int stride, float temp, float *output, tree hier) +{ + int *tree_groups_size = cuda_make_int_array(hier.group_size, hier.groups); + int *tree_groups_offset = cuda_make_int_array(hier.group_offset, hier.groups); + /* + static int *tree_groups_size = 0; + static int *tree_groups_offset = 0; + if(!tree_groups_size){ + tree_groups_size = cuda_make_int_array(hier.group_size, hier.groups); + tree_groups_offset = cuda_make_int_array(hier.group_offset, hier.groups); + } + */ + int num = spatial*batch*hier.groups; + softmax_tree_kernel<<>>(input, spatial, batch, stride, temp, output, hier.groups, tree_groups_size, tree_groups_offset); + check_error(cudaPeekAtLastError()); + cuda_free((float *)tree_groups_size); + cuda_free((float *)tree_groups_offset); +} + +__global__ void softmax_kernel(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output) +{ + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (id >= batch*groups) return; + int b = id / groups; + int g = id % groups; + softmax_device(input + b*batch_offset + g*group_offset, n, temp, stride, output + b*batch_offset + g*group_offset); } -extern "C" void softmax_gpu(float *input, int n, int offset, int groups, float temp, float *output) +extern "C" void softmax_gpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output) +{ + softmax_kernel<<>>(input, n, batch, batch_offset, groups, group_offset, stride, temp, output); + check_error(cudaPeekAtLastError()); +} + + +__global__ void upsample_kernel(size_t N, float *x, int w, int h, int c, int batch, int stride, int forward, float scale, float *out) +{ + size_t i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i >= N) return; + int out_index = i; + int out_w = i%(w*stride); + i = i/(w*stride); + int out_h = i%(h*stride); + i = i/(h*stride); + int out_c = i%c; + i = i/c; + int b = i%batch; + + int in_w = out_w / stride; + int in_h = out_h / stride; + int in_c = out_c; + + int in_index = b*w*h*c + in_c*w*h + in_h*w + in_w; + + + if(forward) out[out_index] += scale * x[in_index]; + else atomicAdd(x+in_index, scale * out[out_index]); +} +extern "C" void upsample_gpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out) { - int inputs = n; - int batch = groups; - softmax_kernel<<>>(inputs, offset, batch, input, temp, output); + size_t size = w*h*c*batch*stride*stride; + upsample_kernel<<>>(size, in, w, h, c, batch, stride, forward, scale, out); check_error(cudaPeekAtLastError()); } diff --git a/image.darknet/src/box.c b/image.darknet/src/box.c index 39dea06..8a1772c 100644 --- a/image.darknet/src/box.c +++ b/image.darknet/src/box.c @@ -3,13 +3,98 @@ #include #include -box float_to_box(float *f) +int nms_comparator(const void *pa, const void *pb) { - box b; + detection a = *(detection *)pa; + detection b = *(detection *)pb; + float diff = 0; + if(b.sort_class >= 0){ + diff = a.prob[b.sort_class] - b.prob[b.sort_class]; + } else { + diff = a.objectness - b.objectness; + } + if(diff < 0) return 1; + else if(diff > 0) return -1; + return 0; +} + +void do_nms_obj(detection *dets, int total, int classes, float thresh) +{ + int i, j, k; + k = total-1; + for(i = 0; i <= k; ++i){ + if(dets[i].objectness == 0){ + detection swap = dets[i]; + dets[i] = dets[k]; + dets[k] = swap; + --k; + --i; + } + } + total = k+1; + + for(i = 0; i < total; ++i){ + dets[i].sort_class = -1; + } + + qsort(dets, total, sizeof(detection), nms_comparator); + for(i = 0; i < total; ++i){ + if(dets[i].objectness == 0) continue; + box a = dets[i].bbox; + for(j = i+1; j < total; ++j){ + if(dets[j].objectness == 0) continue; + box b = dets[j].bbox; + if (box_iou(a, b) > thresh){ + dets[j].objectness = 0; + for(k = 0; k < classes; ++k){ + dets[j].prob[k] = 0; + } + } + } + } +} + + +void do_nms_sort(detection *dets, int total, int classes, float thresh) +{ + int i, j, k; + k = total-1; + for(i = 0; i <= k; ++i){ + if(dets[i].objectness == 0){ + detection swap = dets[i]; + dets[i] = dets[k]; + dets[k] = swap; + --k; + --i; + } + } + total = k+1; + + for(k = 0; k < classes; ++k){ + for(i = 0; i < total; ++i){ + dets[i].sort_class = k; + } + qsort(dets, total, sizeof(detection), nms_comparator); + for(i = 0; i < total; ++i){ + if(dets[i].prob[k] == 0) continue; + box a = dets[i].bbox; + for(j = i+1; j < total; ++j){ + box b = dets[j].bbox; + if (box_iou(a, b) > thresh){ + dets[j].prob[k] = 0; + } + } + } + } +} + +box float_to_box(float *f, int stride) +{ + box b = {0}; b.x = f[0]; - b.y = f[1]; - b.w = f[2]; - b.h = f[3]; + b.y = f[1*stride]; + b.w = f[2*stride]; + b.h = f[3*stride]; return b; } @@ -230,79 +315,6 @@ dbox diou(box a, box b) return dd; } -typedef struct{ - int index; - int class; - float **probs; -} sortable_bbox; - -int nms_comparator(const void *pa, const void *pb) -{ - sortable_bbox a = *(sortable_bbox *)pa; - sortable_bbox b = *(sortable_bbox *)pb; - float diff = a.probs[a.index][b.class] - b.probs[b.index][b.class]; - if(diff < 0) return 1; - else if(diff > 0) return -1; - return 0; -} - -void do_nms_obj(box *boxes, float **probs, int total, int classes, float thresh) -{ - int i, j, k; - sortable_bbox *s = calloc(total, sizeof(sortable_bbox)); - - for(i = 0; i < total; ++i){ - s[i].index = i; - s[i].class = classes; - s[i].probs = probs; - } - - qsort(s, total, sizeof(sortable_bbox), nms_comparator); - for(i = 0; i < total; ++i){ - if(probs[s[i].index][classes] == 0) continue; - box a = boxes[s[i].index]; - for(j = i+1; j < total; ++j){ - box b = boxes[s[j].index]; - if (box_iou(a, b) > thresh){ - for(k = 0; k < classes+1; ++k){ - probs[s[j].index][k] = 0; - } - } - } - } - free(s); -} - - -void do_nms_sort(box *boxes, float **probs, int total, int classes, float thresh) -{ - int i, j, k; - sortable_bbox *s = calloc(total, sizeof(sortable_bbox)); - - for(i = 0; i < total; ++i){ - s[i].index = i; - s[i].class = 0; - s[i].probs = probs; - } - - for(k = 0; k < classes; ++k){ - for(i = 0; i < total; ++i){ - s[i].class = k; - } - qsort(s, total, sizeof(sortable_bbox), nms_comparator); - for(i = 0; i < total; ++i){ - if(probs[s[i].index][k] == 0) continue; - box a = boxes[s[i].index]; - for(j = i+1; j < total; ++j){ - box b = boxes[s[j].index]; - if (box_iou(a, b) > thresh){ - probs[s[j].index][k] = 0; - } - } - } - } - free(s); -} void do_nms(box *boxes, float **probs, int total, int classes, float thresh) { diff --git a/image.darknet/src/box.h b/image.darknet/src/box.h index c65589b..dda3e59 100644 --- a/image.darknet/src/box.h +++ b/image.darknet/src/box.h @@ -1,21 +1,13 @@ #ifndef BOX_H #define BOX_H - -typedef struct{ - float x, y, w, h; -} box; +#include "darknet.h" typedef struct{ float dx, dy, dw, dh; } dbox; -box float_to_box(float *f); -float box_iou(box a, box b); float box_rmse(box a, box b); dbox diou(box a, box b); -void do_nms(box *boxes, float **probs, int total, int classes, float thresh); -void do_nms_sort(box *boxes, float **probs, int total, int classes, float thresh); -void do_nms_obj(box *boxes, float **probs, int total, int classes, float thresh); box decode_box(box b, box anchor); box encode_box(box b, box anchor); diff --git a/image.darknet/src/captcha.c b/image.darknet/src/captcha.c deleted file mode 100644 index 3d449b2..0000000 --- a/image.darknet/src/captcha.c +++ /dev/null @@ -1,364 +0,0 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" - -void fix_data_captcha(data d, int mask) -{ - matrix labels = d.y; - int i, j; - for(i = 0; i < d.y.rows; ++i){ - for(j = 0; j < d.y.cols; j += 2){ - if (mask){ - if(!labels.vals[i][j]){ - labels.vals[i][j] = SECRET_NUM; - labels.vals[i][j+1] = SECRET_NUM; - }else if(labels.vals[i][j+1]){ - labels.vals[i][j] = 0; - } - } else{ - if (labels.vals[i][j]) { - labels.vals[i][j+1] = 0; - } else { - labels.vals[i][j+1] = 1; - } - } - } - } -} - -void train_captcha(char *cfgfile, char *weightfile) -{ - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = 1024; - int i = *net.seen/imgs; - int solved = 1; - list *plist; - char **labels = get_labels("/data/captcha/reimgs.labels.list"); - if (solved){ - plist = get_paths("/data/captcha/reimgs.solved.list"); - }else{ - plist = get_paths("/data/captcha/reimgs.raw.list"); - } - char **paths = (char **)list_to_array(plist); - printf("%d\n", plist->size); - clock_t time; - pthread_t load_thread; - data train; - data buffer; - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.paths = paths; - args.classes = 26; - args.n = imgs; - args.m = plist->size; - args.labels = labels; - args.d = &buffer; - args.type = CLASSIFICATION_DATA; - - load_thread = load_data_in_thread(args); - while(1){ - ++i; - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - fix_data_captcha(train, solved); - - /* - image im = float_to_image(256, 256, 3, train.X.vals[114]); - show_image(im, "training"); - cvWaitKey(0); - */ - - load_thread = load_data_in_thread(args); - printf("Loaded: %lf seconds\n", sec(clock()-time)); - time=clock(); - float loss = train_network(net, train); - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), *net.seen); - free_data(train); - if(i%100==0){ - char buff[256]; - sprintf(buff, "/home/pjreddie/imagenet_backup/%s_%d.weights",base, i); - save_weights(net, buff); - } - } -} - -void test_captcha(char *cfgfile, char *weightfile, char *filename) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - int i = 0; - char **names = get_labels("/data/captcha/reimgs.labels.list"); - char buff[256]; - char *input = buff; - int indexes[26]; - 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, "\n"); - } - image im = load_image_color(input, net.w, net.h); - float *X = im.data; - float *predictions = network_predict(net, X); - top_predictions(net, 26, indexes); - //printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - for(i = 0; i < 26; ++i){ - int index = indexes[i]; - if(i != 0) printf(", "); - printf("%s %f", names[index], predictions[index]); - } - printf("\n"); - fflush(stdout); - free_image(im); - if (filename) break; - } -} - -void valid_captcha(char *cfgfile, char *weightfile, char *filename) -{ - char **labels = get_labels("/data/captcha/reimgs.labels.list"); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - list *plist = get_paths("/data/captcha/reimgs.fg.list"); - char **paths = (char **)list_to_array(plist); - int N = plist->size; - int outputs = net.outputs; - - set_batch_network(&net, 1); - srand(2222222); - int i, j; - for(i = 0; i < N; ++i){ - if (i%100 == 0) fprintf(stderr, "%d\n", i); - image im = load_image_color(paths[i], net.w, net.h); - float *X = im.data; - float *predictions = network_predict(net, X); - //printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - int truth = -1; - for(j = 0; j < 13; ++j){ - if (strstr(paths[i], labels[j])) truth = j; - } - if (truth == -1){ - fprintf(stderr, "bad: %s\n", paths[i]); - return; - } - printf("%d, ", truth); - for(j = 0; j < outputs; ++j){ - if (j != 0) printf(", "); - printf("%f", predictions[j]); - } - printf("\n"); - fflush(stdout); - free_image(im); - if (filename) break; - } -} - -/* - void train_captcha(char *cfgfile, char *weightfile) - { - float avg_loss = -1; - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = 1024; - int i = net.seen/imgs; - list *plist = get_paths("/data/captcha/train.auto5"); - char **paths = (char **)list_to_array(plist); - printf("%d\n", plist->size); - clock_t time; - while(1){ - ++i; - time=clock(); - data train = load_data_captcha(paths, imgs, plist->size, 10, 200, 60); - translate_data_rows(train, -128); - scale_data_rows(train, 1./128); - printf("Loaded: %lf seconds\n", sec(clock()-time)); - time=clock(); - float loss = train_network(net, train); - net.seen += imgs; - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), net.seen); - free_data(train); - if(i%10==0){ - char buff[256]; - sprintf(buff, "/home/pjreddie/imagenet_backup/%s_%d.weights",base, i); - save_weights(net, buff); - } - } - } - - void decode_captcha(char *cfgfile, char *weightfile) - { - setbuf(stdout, NULL); - srand(time(0)); - network net = parse_network_cfg(cfgfile); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } - char filename[256]; - while(1){ - printf("Enter filename: "); - fgets(filename, 256, stdin); - strtok(filename, "\n"); - image im = load_image_color(filename, 300, 57); - scale_image(im, 1./255.); - float *X = im.data; - float *predictions = network_predict(net, X); - image out = float_to_image(300, 57, 1, predictions); - show_image(out, "decoded"); -#ifdef OPENCV -cvWaitKey(0); -#endif -free_image(im); -} -} - -void encode_captcha(char *cfgfile, char *weightfile) -{ -float avg_loss = -1; -srand(time(0)); -char *base = basecfg(cfgfile); -printf("%s\n", base); -network net = parse_network_cfg(cfgfile); -if(weightfile){ - load_weights(&net, weightfile); -} -printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); -int imgs = 1024; -int i = net.seen/imgs; -list *plist = get_paths("/data/captcha/encode.list"); -char **paths = (char **)list_to_array(plist); -printf("%d\n", plist->size); -clock_t time; -while(1){ - ++i; - time=clock(); - data train = load_data_captcha_encode(paths, imgs, plist->size, 300, 57); - scale_data_rows(train, 1./255); - printf("Loaded: %lf seconds\n", sec(clock()-time)); - time=clock(); - float loss = train_network(net, train); - net.seen += imgs; - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), net.seen); - free_matrix(train.X); - if(i%100==0){ - char buff[256]; - sprintf(buff, "/home/pjreddie/imagenet_backup/%s_%d.weights",base, i); - save_weights(net, buff); - } -} -} - -void validate_captcha(char *cfgfile, char *weightfile) -{ - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int numchars = 37; - list *plist = get_paths("/data/captcha/solved.hard"); - char **paths = (char **)list_to_array(plist); - int imgs = plist->size; - data valid = load_data_captcha(paths, imgs, 0, 10, 200, 60); - translate_data_rows(valid, -128); - scale_data_rows(valid, 1./128); - matrix pred = network_predict_data(net, valid); - int i, k; - int correct = 0; - int total = 0; - int accuracy = 0; - for(i = 0; i < imgs; ++i){ - int allcorrect = 1; - for(k = 0; k < 10; ++k){ - char truth = int_to_alphanum(max_index(valid.y.vals[i]+k*numchars, numchars)); - char prediction = int_to_alphanum(max_index(pred.vals[i]+k*numchars, numchars)); - if (truth != prediction) allcorrect=0; - if (truth != '.' && truth == prediction) ++correct; - if (truth != '.' || truth != prediction) ++total; - } - accuracy += allcorrect; - } - printf("Word Accuracy: %f, Char Accuracy %f\n", (float)accuracy/imgs, (float)correct/total); - free_data(valid); -} - -void test_captcha(char *cfgfile, char *weightfile) -{ - setbuf(stdout, NULL); - srand(time(0)); - //char *base = basecfg(cfgfile); - //printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } - char filename[256]; - while(1){ - //printf("Enter filename: "); - fgets(filename, 256, stdin); - strtok(filename, "\n"); - image im = load_image_color(filename, 200, 60); - translate_image(im, -128); - scale_image(im, 1/128.); - float *X = im.data; - float *predictions = network_predict(net, X); - print_letters(predictions, 10); - free_image(im); - } -} - */ -void run_captcha(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *filename = (argc > 5) ? argv[5]: 0; - if(0==strcmp(argv[2], "train")) train_captcha(cfg, weights); - else if(0==strcmp(argv[2], "test")) test_captcha(cfg, weights, filename); - else if(0==strcmp(argv[2], "valid")) valid_captcha(cfg, weights, filename); - //if(0==strcmp(argv[2], "test")) test_captcha(cfg, weights); - //else if(0==strcmp(argv[2], "encode")) encode_captcha(cfg, weights); - //else if(0==strcmp(argv[2], "decode")) decode_captcha(cfg, weights); - //else if(0==strcmp(argv[2], "valid")) validate_captcha(cfg, weights); -} - diff --git a/image.darknet/src/cifar.c b/image.darknet/src/cifar.c deleted file mode 100644 index d0ac459..0000000 --- a/image.darknet/src/cifar.c +++ /dev/null @@ -1,277 +0,0 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" -#include "option_list.h" -#include "blas.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -void train_cifar(char *cfgfile, char *weightfile) -{ - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - - char *backup_directory = "/home/pjreddie/backup/"; - int classes = 10; - int N = 50000; - - char **labels = get_labels("data/cifar/labels.txt"); - int epoch = (*net.seen)/N; - data train = load_all_cifar10(); - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - clock_t time=clock(); - - float loss = train_network_sgd(net, train, 1); - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.95 + loss*.05; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; - char buff[256]; - sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); - save_weights(net, buff); - } - if(get_current_batch(net)%100 == 0){ - char buff[256]; - sprintf(buff, "%s/%s.backup",backup_directory,base); - save_weights(net, buff); - } - } - char buff[256]; - sprintf(buff, "%s/%s.weights", backup_directory, base); - save_weights(net, buff); - - free_network(net); - free_ptrs((void**)labels, classes); - free(base); - free_data(train); -} - -void train_cifar_distill(char *cfgfile, char *weightfile) -{ - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - - char *backup_directory = "/home/pjreddie/backup/"; - int classes = 10; - int N = 50000; - - char **labels = get_labels("data/cifar/labels.txt"); - int epoch = (*net.seen)/N; - - data train = load_all_cifar10(); - matrix soft = csv_to_matrix("results/ensemble.csv"); - - float weight = .9; - scale_matrix(soft, weight); - scale_matrix(train.y, 1. - weight); - matrix_add_matrix(soft, train.y); - - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - clock_t time=clock(); - - float loss = train_network_sgd(net, train, 1); - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.95 + loss*.05; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; - char buff[256]; - sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); - save_weights(net, buff); - } - if(get_current_batch(net)%100 == 0){ - char buff[256]; - sprintf(buff, "%s/%s.backup",backup_directory,base); - save_weights(net, buff); - } - } - char buff[256]; - sprintf(buff, "%s/%s.weights", backup_directory, base); - save_weights(net, buff); - - free_network(net); - free_ptrs((void**)labels, classes); - free(base); - free_data(train); -} - -void test_cifar_multi(char *filename, char *weightfile) -{ - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(time(0)); - - float avg_acc = 0; - data test = load_cifar10_data("data/cifar/cifar-10-batches-bin/test_batch.bin"); - - int i; - for(i = 0; i < test.X.rows; ++i){ - image im = float_to_image(32, 32, 3, test.X.vals[i]); - - float pred[10] = {0}; - - float *p = network_predict(net, im.data); - axpy_cpu(10, 1, p, 1, pred, 1); - flip_image(im); - p = network_predict(net, im.data); - axpy_cpu(10, 1, p, 1, pred, 1); - - int index = max_index(pred, 10); - int class = max_index(test.y.vals[i], 10); - if(index == class) avg_acc += 1; - free_image(im); - printf("%4d: %.2f%%\n", i, 100.*avg_acc/(i+1)); - } -} - -void test_cifar(char *filename, char *weightfile) -{ - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - clock_t time; - float avg_acc = 0; - float avg_top5 = 0; - data test = load_cifar10_data("data/cifar/cifar-10-batches-bin/test_batch.bin"); - - time=clock(); - - float *acc = network_accuracies(net, test, 2); - avg_acc += acc[0]; - avg_top5 += acc[1]; - printf("top1: %f, %lf seconds, %d images\n", avg_acc, sec(clock()-time), test.X.rows); - free_data(test); -} - -void extract_cifar() -{ -char *labels[] = {"airplane","automobile","bird","cat","deer","dog","frog","horse","ship","truck"}; - int i; - data train = load_all_cifar10(); - data test = load_cifar10_data("data/cifar/cifar-10-batches-bin/test_batch.bin"); - for(i = 0; i < train.X.rows; ++i){ - image im = float_to_image(32, 32, 3, train.X.vals[i]); - int class = max_index(train.y.vals[i], 10); - char buff[256]; - sprintf(buff, "data/cifar/train/%d_%s",i,labels[class]); - save_image_png(im, buff); - } - for(i = 0; i < test.X.rows; ++i){ - image im = float_to_image(32, 32, 3, test.X.vals[i]); - int class = max_index(test.y.vals[i], 10); - char buff[256]; - sprintf(buff, "data/cifar/test/%d_%s",i,labels[class]); - save_image_png(im, buff); - } -} - -void test_cifar_csv(char *filename, char *weightfile) -{ - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - data test = load_cifar10_data("data/cifar/cifar-10-batches-bin/test_batch.bin"); - - matrix pred = network_predict_data(net, test); - - int i; - for(i = 0; i < test.X.rows; ++i){ - image im = float_to_image(32, 32, 3, test.X.vals[i]); - flip_image(im); - } - matrix pred2 = network_predict_data(net, test); - scale_matrix(pred, .5); - scale_matrix(pred2, .5); - matrix_add_matrix(pred2, pred); - - matrix_to_csv(pred); - fprintf(stderr, "Accuracy: %f\n", matrix_topk_accuracy(test.y, pred, 1)); - free_data(test); -} - -void test_cifar_csvtrain(char *filename, char *weightfile) -{ - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - data test = load_all_cifar10(); - - matrix pred = network_predict_data(net, test); - - int i; - for(i = 0; i < test.X.rows; ++i){ - image im = float_to_image(32, 32, 3, test.X.vals[i]); - flip_image(im); - } - matrix pred2 = network_predict_data(net, test); - scale_matrix(pred, .5); - scale_matrix(pred2, .5); - matrix_add_matrix(pred2, pred); - - matrix_to_csv(pred); - fprintf(stderr, "Accuracy: %f\n", matrix_topk_accuracy(test.y, pred, 1)); - free_data(test); -} - -void eval_cifar_csv() -{ - data test = load_cifar10_data("data/cifar/cifar-10-batches-bin/test_batch.bin"); - - matrix pred = csv_to_matrix("results/combined.csv"); - fprintf(stderr, "%d %d\n", pred.rows, pred.cols); - - fprintf(stderr, "Accuracy: %f\n", matrix_topk_accuracy(test.y, pred, 1)); - free_data(test); - free_matrix(pred); -} - - -void run_cifar(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - if(0==strcmp(argv[2], "train")) train_cifar(cfg, weights); - else if(0==strcmp(argv[2], "extract")) extract_cifar(); - else if(0==strcmp(argv[2], "distill")) train_cifar_distill(cfg, weights); - else if(0==strcmp(argv[2], "test")) test_cifar(cfg, weights); - else if(0==strcmp(argv[2], "multi")) test_cifar_multi(cfg, weights); - else if(0==strcmp(argv[2], "csv")) test_cifar_csv(cfg, weights); - else if(0==strcmp(argv[2], "csvtrain")) test_cifar_csvtrain(cfg, weights); - else if(0==strcmp(argv[2], "eval")) eval_cifar_csv(); -} - - diff --git a/image.darknet/src/classifier.c b/image.darknet/src/classifier.c deleted file mode 100644 index 586530a..0000000 --- a/image.darknet/src/classifier.c +++ /dev/null @@ -1,1167 +0,0 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" -#include "option_list.h" -#include "blas.h" -#include "assert.h" -#include "classifier.h" -#include "cuda.h" -#include - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -image get_image_from_stream(CvCapture *cap); -#endif - -float *get_regression_values(char **labels, int n) -{ - float *v = calloc(n, sizeof(float)); - int i; - for(i = 0; i < n; ++i){ - char *p = strchr(labels[i], ' '); - *p = 0; - v[i] = atof(p+1); - } - return v; -} - -void train_classifier(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear) -{ - int i; - - float avg_loss = -1; - char *base = basecfg(cfgfile); - printf("%s\n", base); - printf("%d\n", ngpus); - network *nets = calloc(ngpus, sizeof(network)); - - srand(time(0)); - int seed = rand(); - for(i = 0; i < ngpus; ++i){ - srand(seed); -#ifdef GPU - cuda_set_device(gpus[i]); -#endif - nets[i] = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&nets[i], weightfile); - } - if(clear) *nets[i].seen = 0; - nets[i].learning_rate *= ngpus; - } - srand(time(0)); - network net = nets[0]; - - int imgs = net.batch * net.subdivisions * ngpus; - - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - list *options = read_data_cfg(datacfg); - - char *backup_directory = option_find_str(options, "backup", "/backup/"); - char *label_list = option_find_str(options, "labels", "data/labels.list"); - char *train_list = option_find_str(options, "train", "data/train.list"); - int classes = option_find_int(options, "classes", 2); - - char **labels = get_labels(label_list); - list *plist = get_paths(train_list); - char **paths = (char **)list_to_array(plist); - printf("%d\n", plist->size); - int N = plist->size; - clock_t time; - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.threads = 32; - args.hierarchy = net.hierarchy; - - args.min = net.min_crop; - args.max = net.max_crop; - args.angle = net.angle; - args.aspect = net.aspect; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; - args.size = net.w; - - args.paths = paths; - args.classes = classes; - args.n = imgs; - args.m = N; - args.labels = labels; - args.type = CLASSIFICATION_DATA; - - data train; - data buffer; - pthread_t load_thread; - args.d = &buffer; - load_thread = load_data(args); - - int epoch = (*net.seen)/N; - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - time=clock(); - - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data(args); - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - time=clock(); - - float loss = 0; -#ifdef GPU - if(ngpus == 1){ - loss = train_network(net, train); - } else { - loss = train_networks(nets, ngpus, train, 4); - } -#else - loss = train_network(net, train); -#endif - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); - free_data(train); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; - char buff[256]; - sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); - save_weights(net, buff); - } - if(get_current_batch(net)%100 == 0){ - char buff[256]; - sprintf(buff, "%s/%s.backup",backup_directory,base); - save_weights(net, buff); - } - } - char buff[256]; - sprintf(buff, "%s/%s.weights", backup_directory, base); - save_weights(net, buff); - - free_network(net); - free_ptrs((void**)labels, classes); - free_ptrs((void**)paths, plist->size); - free_list(plist); - free(base); -} - - -/* - void train_classifier(char *datacfg, char *cfgfile, char *weightfile, int clear) - { - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - if(clear) *net.seen = 0; - - int imgs = net.batch * net.subdivisions; - - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - list *options = read_data_cfg(datacfg); - - char *backup_directory = option_find_str(options, "backup", "/backup/"); - char *label_list = option_find_str(options, "labels", "data/labels.list"); - char *train_list = option_find_str(options, "train", "data/train.list"); - int classes = option_find_int(options, "classes", 2); - - char **labels = get_labels(label_list); - list *plist = get_paths(train_list); - char **paths = (char **)list_to_array(plist); - printf("%d\n", plist->size); - int N = plist->size; - clock_t time; - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.threads = 8; - - args.min = net.min_crop; - args.max = net.max_crop; - args.angle = net.angle; - args.aspect = net.aspect; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; - args.size = net.w; - args.hierarchy = net.hierarchy; - - args.paths = paths; - args.classes = classes; - args.n = imgs; - args.m = N; - args.labels = labels; - args.type = CLASSIFICATION_DATA; - - data train; - data buffer; - pthread_t load_thread; - args.d = &buffer; - load_thread = load_data(args); - - int epoch = (*net.seen)/N; - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - time=clock(); - - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data(args); - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - time=clock(); - -#ifdef OPENCV -if(0){ -int u; -for(u = 0; u < imgs; ++u){ - image im = float_to_image(net.w, net.h, 3, train.X.vals[u]); - show_image(im, "loaded"); - cvWaitKey(0); -} -} -#endif - -float loss = train_network(net, train); -free_data(train); - -if(avg_loss == -1) avg_loss = loss; -avg_loss = avg_loss*.9 + loss*.1; -printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); -if(*net.seen/N > epoch){ - epoch = *net.seen/N; - char buff[256]; - sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); - save_weights(net, buff); -} -if(get_current_batch(net)%100 == 0){ - char buff[256]; - sprintf(buff, "%s/%s.backup",backup_directory,base); - save_weights(net, buff); -} -} -char buff[256]; -sprintf(buff, "%s/%s.weights", backup_directory, base); -save_weights(net, buff); - -free_network(net); -free_ptrs((void**)labels, classes); -free_ptrs((void**)paths, plist->size); -free_list(plist); -free(base); -} -*/ - -void validate_classifier_crop(char *datacfg, char *filename, char *weightfile) -{ - int i = 0; - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - list *options = read_data_cfg(datacfg); - - char *label_list = option_find_str(options, "labels", "data/labels.list"); - char *valid_list = option_find_str(options, "valid", "data/train.list"); - int classes = option_find_int(options, "classes", 2); - int topk = option_find_int(options, "top", 1); - - char **labels = get_labels(label_list); - list *plist = get_paths(valid_list); - - char **paths = (char **)list_to_array(plist); - int m = plist->size; - free_list(plist); - - clock_t time; - float avg_acc = 0; - float avg_topk = 0; - int splits = m/1000; - int num = (i+1)*m/splits - i*m/splits; - - data val, buffer; - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - - args.paths = paths; - args.classes = classes; - args.n = num; - args.m = 0; - args.labels = labels; - args.d = &buffer; - args.type = OLD_CLASSIFICATION_DATA; - - pthread_t load_thread = load_data_in_thread(args); - for(i = 1; i <= splits; ++i){ - time=clock(); - - pthread_join(load_thread, 0); - val = buffer; - - num = (i+1)*m/splits - i*m/splits; - char **part = paths+(i*m/splits); - if(i != splits){ - args.paths = part; - load_thread = load_data_in_thread(args); - } - printf("Loaded: %d images in %lf seconds\n", val.X.rows, sec(clock()-time)); - - time=clock(); - float *acc = network_accuracies(net, val, topk); - avg_acc += acc[0]; - avg_topk += acc[1]; - printf("%d: top 1: %f, top %d: %f, %lf seconds, %d images\n", i, avg_acc/i, topk, avg_topk/i, sec(clock()-time), val.X.rows); - free_data(val); - } -} - -void validate_classifier_10(char *datacfg, char *filename, char *weightfile) -{ - int i, j; - network net = parse_network_cfg(filename); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - list *options = read_data_cfg(datacfg); - - char *label_list = option_find_str(options, "labels", "data/labels.list"); - char *valid_list = option_find_str(options, "valid", "data/train.list"); - int classes = option_find_int(options, "classes", 2); - int topk = option_find_int(options, "top", 1); - - char **labels = get_labels(label_list); - list *plist = get_paths(valid_list); - - char **paths = (char **)list_to_array(plist); - int m = plist->size; - free_list(plist); - - float avg_acc = 0; - float avg_topk = 0; - int *indexes = calloc(topk, sizeof(int)); - - for(i = 0; i < m; ++i){ - int class = -1; - char *path = paths[i]; - for(j = 0; j < classes; ++j){ - if(strstr(path, labels[j])){ - class = j; - break; - } - } - int w = net.w; - int h = net.h; - int shift = 32; - image im = load_image_color(paths[i], w+shift, h+shift); - image images[10]; - images[0] = crop_image(im, -shift, -shift, w, h); - images[1] = crop_image(im, shift, -shift, w, h); - images[2] = crop_image(im, 0, 0, w, h); - images[3] = crop_image(im, -shift, shift, w, h); - images[4] = crop_image(im, shift, shift, w, h); - flip_image(im); - images[5] = crop_image(im, -shift, -shift, w, h); - images[6] = crop_image(im, shift, -shift, w, h); - images[7] = crop_image(im, 0, 0, w, h); - images[8] = crop_image(im, -shift, shift, w, h); - images[9] = crop_image(im, shift, shift, w, h); - float *pred = calloc(classes, sizeof(float)); - for(j = 0; j < 10; ++j){ - float *p = network_predict(net, images[j].data); - if(net.hierarchy) hierarchy_predictions(p, net.outputs, net.hierarchy, 1); - axpy_cpu(classes, 1, p, 1, pred, 1); - free_image(images[j]); - } - free_image(im); - top_k(pred, classes, topk, indexes); - free(pred); - if(indexes[0] == class) avg_acc += 1; - for(j = 0; j < topk; ++j){ - if(indexes[j] == class) avg_topk += 1; - } - - printf("%d: top 1: %f, top %d: %f\n", i, avg_acc/(i+1), topk, avg_topk/(i+1)); - } -} - -void validate_classifier_full(char *datacfg, char *filename, char *weightfile) -{ - int i, j; - network net = parse_network_cfg(filename); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - list *options = read_data_cfg(datacfg); - - char *label_list = option_find_str(options, "labels", "data/labels.list"); - char *valid_list = option_find_str(options, "valid", "data/train.list"); - int classes = option_find_int(options, "classes", 2); - int topk = option_find_int(options, "top", 1); - - char **labels = get_labels(label_list); - list *plist = get_paths(valid_list); - - char **paths = (char **)list_to_array(plist); - int m = plist->size; - free_list(plist); - - float avg_acc = 0; - float avg_topk = 0; - int *indexes = calloc(topk, sizeof(int)); - - int size = net.w; - for(i = 0; i < m; ++i){ - int class = -1; - char *path = paths[i]; - for(j = 0; j < classes; ++j){ - if(strstr(path, labels[j])){ - class = j; - break; - } - } - image im = load_image_color(paths[i], 0, 0); - image resized = resize_min(im, size); - resize_network(&net, resized.w, resized.h); - //show_image(im, "orig"); - //show_image(crop, "cropped"); - //cvWaitKey(0); - float *pred = network_predict(net, resized.data); - if(net.hierarchy) hierarchy_predictions(pred, net.outputs, net.hierarchy, 1); - - free_image(im); - free_image(resized); - top_k(pred, classes, topk, indexes); - - if(indexes[0] == class) avg_acc += 1; - for(j = 0; j < topk; ++j){ - if(indexes[j] == class) avg_topk += 1; - } - - printf("%d: top 1: %f, top %d: %f\n", i, avg_acc/(i+1), topk, avg_topk/(i+1)); - } -} - - -void validate_classifier_single(char *datacfg, char *filename, char *weightfile) -{ - int i, j; - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(time(0)); - - list *options = read_data_cfg(datacfg); - - char *label_list = option_find_str(options, "labels", "data/labels.list"); - char *leaf_list = option_find_str(options, "leaves", 0); - if(leaf_list) change_leaves(net.hierarchy, leaf_list); - char *valid_list = option_find_str(options, "valid", "data/train.list"); - int classes = option_find_int(options, "classes", 2); - int topk = option_find_int(options, "top", 1); - - char **labels = get_labels(label_list); - list *plist = get_paths(valid_list); - - char **paths = (char **)list_to_array(plist); - int m = plist->size; - free_list(plist); - - float avg_acc = 0; - float avg_topk = 0; - int *indexes = calloc(topk, sizeof(int)); - - for(i = 0; i < m; ++i){ - int class = -1; - char *path = paths[i]; - for(j = 0; j < classes; ++j){ - if(strstr(path, labels[j])){ - class = j; - break; - } - } - image im = load_image_color(paths[i], 0, 0); - image resized = resize_min(im, net.w); - image crop = crop_image(resized, (resized.w - net.w)/2, (resized.h - net.h)/2, net.w, net.h); - //show_image(im, "orig"); - //show_image(crop, "cropped"); - //cvWaitKey(0); - float *pred = network_predict(net, crop.data); - if(net.hierarchy) hierarchy_predictions(pred, net.outputs, net.hierarchy, 1); - - if(resized.data != im.data) free_image(resized); - free_image(im); - free_image(crop); - top_k(pred, classes, topk, indexes); - - if(indexes[0] == class) avg_acc += 1; - for(j = 0; j < topk; ++j){ - if(indexes[j] == class) avg_topk += 1; - } - - printf("%d: top 1: %f, top %d: %f\n", i, avg_acc/(i+1), topk, avg_topk/(i+1)); - } -} - -void validate_classifier_multi(char *datacfg, char *filename, char *weightfile) -{ - int i, j; - network net = parse_network_cfg(filename); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - list *options = read_data_cfg(datacfg); - - char *label_list = option_find_str(options, "labels", "data/labels.list"); - char *valid_list = option_find_str(options, "valid", "data/train.list"); - int classes = option_find_int(options, "classes", 2); - int topk = option_find_int(options, "top", 1); - - char **labels = get_labels(label_list); - list *plist = get_paths(valid_list); - int scales[] = {224, 288, 320, 352, 384}; - int nscales = sizeof(scales)/sizeof(scales[0]); - - char **paths = (char **)list_to_array(plist); - int m = plist->size; - free_list(plist); - - float avg_acc = 0; - float avg_topk = 0; - int *indexes = calloc(topk, sizeof(int)); - - for(i = 0; i < m; ++i){ - int class = -1; - char *path = paths[i]; - for(j = 0; j < classes; ++j){ - if(strstr(path, labels[j])){ - class = j; - break; - } - } - float *pred = calloc(classes, sizeof(float)); - image im = load_image_color(paths[i], 0, 0); - for(j = 0; j < nscales; ++j){ - image r = resize_min(im, scales[j]); - resize_network(&net, r.w, r.h); - float *p = network_predict(net, r.data); - if(net.hierarchy) hierarchy_predictions(p, net.outputs, net.hierarchy, 1); - axpy_cpu(classes, 1, p, 1, pred, 1); - flip_image(r); - p = network_predict(net, r.data); - axpy_cpu(classes, 1, p, 1, pred, 1); - if(r.data != im.data) free_image(r); - } - free_image(im); - top_k(pred, classes, topk, indexes); - free(pred); - if(indexes[0] == class) avg_acc += 1; - for(j = 0; j < topk; ++j){ - if(indexes[j] == class) avg_topk += 1; - } - - printf("%d: top 1: %f, top %d: %f\n", i, avg_acc/(i+1), topk, avg_topk/(i+1)); - } -} - -void try_classifier(char *datacfg, char *cfgfile, char *weightfile, char *filename, int layer_num) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - - list *options = read_data_cfg(datacfg); - - char *name_list = option_find_str(options, "names", 0); - if(!name_list) name_list = option_find_str(options, "labels", "data/labels.list"); - int top = option_find_int(options, "top", 1); - - int i = 0; - char **names = get_labels(name_list); - clock_t time; - int *indexes = calloc(top, sizeof(int)); - char buff[256]; - char *input = buff; - 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, "\n"); - } - image orig = load_image_color(input, 0, 0); - image r = resize_min(orig, 256); - image im = crop_image(r, (r.w - 224 - 1)/2 + 1, (r.h - 224 - 1)/2 + 1, 224, 224); - float mean[] = {0.48263312050943, 0.45230225481413, 0.40099074308742}; - float std[] = {0.22590347483426, 0.22120921437787, 0.22103996251583}; - float var[3]; - var[0] = std[0]*std[0]; - var[1] = std[1]*std[1]; - var[2] = std[2]*std[2]; - - normalize_cpu(im.data, mean, var, 1, 3, im.w*im.h); - - float *X = im.data; - time=clock(); - float *predictions = network_predict(net, X); - - layer l = net.layers[layer_num]; - for(i = 0; i < l.c; ++i){ - if(l.rolling_mean) printf("%f %f %f\n", l.rolling_mean[i], l.rolling_variance[i], l.scales[i]); - } -#ifdef GPU - cuda_pull_array(l.output_gpu, l.output, l.outputs); -#endif - for(i = 0; i < l.outputs; ++i){ - printf("%f\n", l.output[i]); - } - /* - - printf("\n\nWeights\n"); - for(i = 0; i < l.n*l.size*l.size*l.c; ++i){ - printf("%f\n", l.filters[i]); - } - - printf("\n\nBiases\n"); - for(i = 0; i < l.n; ++i){ - printf("%f\n", l.biases[i]); - } - */ - - top_predictions(net, top, indexes); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - for(i = 0; i < top; ++i){ - int index = indexes[i]; - printf("%s: %f\n", names[index], predictions[index]); - } - free_image(im); - if (filename) break; - } -} - -void predict_classifier(char *datacfg, char *cfgfile, char *weightfile, char *filename, int top) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - - list *options = read_data_cfg(datacfg); - - char *name_list = option_find_str(options, "names", 0); - if(!name_list) name_list = option_find_str(options, "labels", "data/labels.list"); - if(top == 0) top = option_find_int(options, "top", 1); - - int i = 0; - char **names = get_labels(name_list); - clock_t time; - int *indexes = calloc(top, sizeof(int)); - char buff[256]; - char *input = buff; - int size = net.w; - 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, "\n"); - } - image im = load_image_color(input, 0, 0); - image r = resize_min(im, size); - resize_network(&net, r.w, r.h); - printf("%d %d\n", r.w, r.h); - - float *X = r.data; - time=clock(); - float *predictions = network_predict(net, X); - if(net.hierarchy) hierarchy_predictions(predictions, net.outputs, net.hierarchy, 0); - top_k(predictions, net.outputs, top, indexes); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - for(i = 0; i < top; ++i){ - int index = indexes[i]; - if(net.hierarchy) printf("%d, %s: %f, parent: %s \n",index, names[index], predictions[index], (net.hierarchy->parent[index] >= 0) ? names[net.hierarchy->parent[index]] : "Root"); - else printf("%s: %f\n",names[index], predictions[index]); - } - if(r.data != im.data) free_image(r); - free_image(im); - if (filename) break; - } -} - - -void label_classifier(char *datacfg, char *filename, char *weightfile) -{ - int i; - network net = parse_network_cfg(filename); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - list *options = read_data_cfg(datacfg); - - char *label_list = option_find_str(options, "names", "data/labels.list"); - char *test_list = option_find_str(options, "test", "data/train.list"); - int classes = option_find_int(options, "classes", 2); - - char **labels = get_labels(label_list); - list *plist = get_paths(test_list); - - char **paths = (char **)list_to_array(plist); - int m = plist->size; - free_list(plist); - - for(i = 0; i < m; ++i){ - image im = load_image_color(paths[i], 0, 0); - image resized = resize_min(im, net.w); - image crop = crop_image(resized, (resized.w - net.w)/2, (resized.h - net.h)/2, net.w, net.h); - float *pred = network_predict(net, crop.data); - - if(resized.data != im.data) free_image(resized); - free_image(im); - free_image(crop); - int ind = max_index(pred, classes); - - printf("%s\n", labels[ind]); - } -} - - -void test_classifier(char *datacfg, char *cfgfile, char *weightfile, int target_layer) -{ - int curr = 0; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - list *options = read_data_cfg(datacfg); - - char *test_list = option_find_str(options, "test", "data/test.list"); - int classes = option_find_int(options, "classes", 2); - - list *plist = get_paths(test_list); - - char **paths = (char **)list_to_array(plist); - int m = plist->size; - free_list(plist); - - clock_t time; - - data val, buffer; - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.paths = paths; - args.classes = classes; - args.n = net.batch; - args.m = 0; - args.labels = 0; - args.d = &buffer; - args.type = OLD_CLASSIFICATION_DATA; - - pthread_t load_thread = load_data_in_thread(args); - for(curr = net.batch; curr < m; curr += net.batch){ - time=clock(); - - pthread_join(load_thread, 0); - val = buffer; - - if(curr < m){ - args.paths = paths + curr; - if (curr + net.batch > m) args.n = m - curr; - load_thread = load_data_in_thread(args); - } - fprintf(stderr, "Loaded: %d images in %lf seconds\n", val.X.rows, sec(clock()-time)); - - time=clock(); - matrix pred = network_predict_data(net, val); - - int i, j; - if (target_layer >= 0){ - //layer l = net.layers[target_layer]; - } - - for(i = 0; i < pred.rows; ++i){ - printf("%s", paths[curr-net.batch+i]); - for(j = 0; j < pred.cols; ++j){ - printf("\t%g", pred.vals[i][j]); - } - printf("\n"); - } - - free_matrix(pred); - - fprintf(stderr, "%lf seconds, %d images, %d total\n", sec(clock()-time), val.X.rows, curr); - free_data(val); - } -} - - -void threat_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_index, const char *filename) -{ -#ifdef OPENCV - float threat = 0; - float roll = .2; - - printf("Classifier Demo\n"); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - list *options = read_data_cfg(datacfg); - - srand(2222222); - CvCapture * cap; - - if(filename){ - cap = cvCaptureFromFile(filename); - }else{ - cap = cvCaptureFromCAM(cam_index); - } - - int top = option_find_int(options, "top", 1); - - char *name_list = option_find_str(options, "names", 0); - char **names = get_labels(name_list); - - int *indexes = calloc(top, sizeof(int)); - - if(!cap) error("Couldn't connect to webcam.\n"); - //cvNamedWindow("Threat", CV_WINDOW_NORMAL); - //cvResizeWindow("Threat", 512, 512); - float fps = 0; - int i; - - int count = 0; - - while(1){ - ++count; - struct timeval tval_before, tval_after, tval_result; - gettimeofday(&tval_before, NULL); - - image in = get_image_from_stream(cap); - if(!in.data) break; - image in_s = resize_image(in, net.w, net.h); - - image out = in; - int x1 = out.w / 20; - int y1 = out.h / 20; - int x2 = 2*x1; - int y2 = out.h - out.h/20; - - int border = .01*out.h; - int h = y2 - y1 - 2*border; - int w = x2 - x1 - 2*border; - - float *predictions = network_predict(net, in_s.data); - float curr_threat = 0; - if(1){ - curr_threat = predictions[0] * 0 + - predictions[1] * .6 + - predictions[2]; - } else { - curr_threat = predictions[218] + - predictions[539] + - predictions[540] + - predictions[368] + - predictions[369] + - predictions[370]; - } - threat = roll * curr_threat + (1-roll) * threat; - - draw_box_width(out, x2 + border, y1 + .02*h, x2 + .5 * w, y1 + .02*h + border, border, 0,0,0); - if(threat > .97) { - draw_box_width(out, x2 + .5 * w + border, - y1 + .02*h - 2*border, - x2 + .5 * w + 6*border, - y1 + .02*h + 3*border, 3*border, 1,0,0); - } - draw_box_width(out, x2 + .5 * w + border, - y1 + .02*h - 2*border, - x2 + .5 * w + 6*border, - y1 + .02*h + 3*border, .5*border, 0,0,0); - draw_box_width(out, x2 + border, y1 + .42*h, x2 + .5 * w, y1 + .42*h + border, border, 0,0,0); - if(threat > .57) { - draw_box_width(out, x2 + .5 * w + border, - y1 + .42*h - 2*border, - x2 + .5 * w + 6*border, - y1 + .42*h + 3*border, 3*border, 1,1,0); - } - draw_box_width(out, x2 + .5 * w + border, - y1 + .42*h - 2*border, - x2 + .5 * w + 6*border, - y1 + .42*h + 3*border, .5*border, 0,0,0); - - draw_box_width(out, x1, y1, x2, y2, border, 0,0,0); - for(i = 0; i < threat * h ; ++i){ - float ratio = (float) i / h; - float r = (ratio < .5) ? (2*(ratio)) : 1; - float g = (ratio < .5) ? 1 : 1 - 2*(ratio - .5); - draw_box_width(out, x1 + border, y2 - border - i, x2 - border, y2 - border - i, 1, r, g, 0); - } - top_predictions(net, top, indexes); - char buff[256]; - sprintf(buff, "/home/pjreddie/tmp/threat_%06d", count); - //save_image(out, buff); - - printf("\033[2J"); - printf("\033[1;1H"); - printf("\nFPS:%.0f\n",fps); - - for(i = 0; i < top; ++i){ - int index = indexes[i]; - printf("%.1f%%: %s\n", predictions[index]*100, names[index]); - } - - if(1){ - show_image(out, "Threat"); - cvWaitKey(10); - } - free_image(in_s); - free_image(in); - - gettimeofday(&tval_after, NULL); - timersub(&tval_after, &tval_before, &tval_result); - float curr = 1000000.f/((long int)tval_result.tv_usec); - fps = .9*fps + .1*curr; - } -#endif -} - - -void gun_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_index, const char *filename) -{ -#ifdef OPENCV - int bad_cats[] = {218, 539, 540, 1213, 1501, 1742, 1911, 2415, 4348, 19223, 368, 369, 370, 1133, 1200, 1306, 2122, 2301, 2537, 2823, 3179, 3596, 3639, 4489, 5107, 5140, 5289, 6240, 6631, 6762, 7048, 7171, 7969, 7984, 7989, 8824, 8927, 9915, 10270, 10448, 13401, 15205, 18358, 18894, 18895, 19249, 19697}; - - printf("Classifier Demo\n"); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - list *options = read_data_cfg(datacfg); - - srand(2222222); - CvCapture * cap; - - if(filename){ - cap = cvCaptureFromFile(filename); - }else{ - cap = cvCaptureFromCAM(cam_index); - } - - int top = option_find_int(options, "top", 1); - - char *name_list = option_find_str(options, "names", 0); - char **names = get_labels(name_list); - - int *indexes = calloc(top, sizeof(int)); - - if(!cap) error("Couldn't connect to webcam.\n"); - cvNamedWindow("Threat Detection", CV_WINDOW_NORMAL); - cvResizeWindow("Threat Detection", 512, 512); - float fps = 0; - int i; - - while(1){ - struct timeval tval_before, tval_after, tval_result; - gettimeofday(&tval_before, NULL); - - image in = get_image_from_stream(cap); - image in_s = resize_image(in, net.w, net.h); - show_image(in, "Threat Detection"); - - float *predictions = network_predict(net, in_s.data); - top_predictions(net, top, indexes); - - printf("\033[2J"); - printf("\033[1;1H"); - - int threat = 0; - for(i = 0; i < sizeof(bad_cats)/sizeof(bad_cats[0]); ++i){ - int index = bad_cats[i]; - if(predictions[index] > .01){ - printf("Threat Detected!\n"); - threat = 1; - break; - } - } - if(!threat) printf("Scanning...\n"); - for(i = 0; i < sizeof(bad_cats)/sizeof(bad_cats[0]); ++i){ - int index = bad_cats[i]; - if(predictions[index] > .01){ - printf("%s\n", names[index]); - } - } - - free_image(in_s); - free_image(in); - - cvWaitKey(10); - - gettimeofday(&tval_after, NULL); - timersub(&tval_after, &tval_before, &tval_result); - float curr = 1000000.f/((long int)tval_result.tv_usec); - fps = .9*fps + .1*curr; - } -#endif -} - -void demo_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_index, const char *filename) -{ -#ifdef OPENCV - printf("Classifier Demo\n"); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - list *options = read_data_cfg(datacfg); - - srand(2222222); - CvCapture * cap; - - if(filename){ - cap = cvCaptureFromFile(filename); - }else{ - cap = cvCaptureFromCAM(cam_index); - } - - int top = option_find_int(options, "top", 1); - - char *name_list = option_find_str(options, "names", 0); - char **names = get_labels(name_list); - - int *indexes = calloc(top, sizeof(int)); - - if(!cap) error("Couldn't connect to webcam.\n"); - cvNamedWindow("Classifier", CV_WINDOW_NORMAL); - cvResizeWindow("Classifier", 512, 512); - float fps = 0; - int i; - - while(1){ - struct timeval tval_before, tval_after, tval_result; - gettimeofday(&tval_before, NULL); - - image in = get_image_from_stream(cap); - image in_s = resize_image(in, net.w, net.h); - show_image(in, "Classifier"); - - float *predictions = network_predict(net, in_s.data); - if(net.hierarchy) hierarchy_predictions(predictions, net.outputs, net.hierarchy, 1); - top_predictions(net, top, indexes); - - printf("\033[2J"); - printf("\033[1;1H"); - printf("\nFPS:%.0f\n",fps); - - for(i = 0; i < top; ++i){ - int index = indexes[i]; - printf("%.1f%%: %s\n", predictions[index]*100, names[index]); - } - - free_image(in_s); - free_image(in); - - cvWaitKey(10); - - gettimeofday(&tval_after, NULL); - timersub(&tval_after, &tval_before, &tval_result); - float curr = 1000000.f/((long int)tval_result.tv_usec); - fps = .9*fps + .1*curr; - } -#endif -} - - -void run_classifier(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); - int *gpus = 0; - int gpu = 0; - int ngpus = 0; - if(gpu_list){ - printf("%s\n", gpu_list); - int len = strlen(gpu_list); - ngpus = 1; - int i; - for(i = 0; i < len; ++i){ - if (gpu_list[i] == ',') ++ngpus; - } - gpus = calloc(ngpus, sizeof(int)); - for(i = 0; i < ngpus; ++i){ - gpus[i] = atoi(gpu_list); - gpu_list = strchr(gpu_list, ',')+1; - } - } else { - gpu = gpu_index; - gpus = &gpu; - ngpus = 1; - } - - int cam_index = find_int_arg(argc, argv, "-c", 0); - int top = find_int_arg(argc, argv, "-t", 0); - int clear = find_arg(argc, argv, "-clear"); - char *data = argv[3]; - char *cfg = argv[4]; - char *weights = (argc > 5) ? argv[5] : 0; - char *filename = (argc > 6) ? argv[6]: 0; - char *layer_s = (argc > 7) ? argv[7]: 0; - int layer = layer_s ? atoi(layer_s) : -1; - if(0==strcmp(argv[2], "predict")) predict_classifier(data, cfg, weights, filename, top); - else if(0==strcmp(argv[2], "try")) try_classifier(data, cfg, weights, filename, atoi(layer_s)); - else if(0==strcmp(argv[2], "train")) train_classifier(data, cfg, weights, gpus, ngpus, clear); - else if(0==strcmp(argv[2], "demo")) demo_classifier(data, cfg, weights, cam_index, filename); - else if(0==strcmp(argv[2], "gun")) gun_classifier(data, cfg, weights, cam_index, filename); - else if(0==strcmp(argv[2], "threat")) threat_classifier(data, cfg, weights, cam_index, filename); - else if(0==strcmp(argv[2], "test")) test_classifier(data, cfg, weights, layer); - else if(0==strcmp(argv[2], "label")) label_classifier(data, cfg, weights); - else if(0==strcmp(argv[2], "valid")) validate_classifier_single(data, cfg, weights); - else if(0==strcmp(argv[2], "validmulti")) validate_classifier_multi(data, cfg, weights); - else if(0==strcmp(argv[2], "valid10")) validate_classifier_10(data, cfg, weights); - else if(0==strcmp(argv[2], "validcrop")) validate_classifier_crop(data, cfg, weights); - else if(0==strcmp(argv[2], "validfull")) validate_classifier_full(data, cfg, weights); -} - - diff --git a/image.darknet/src/classifier.h b/image.darknet/src/classifier.h index 3c89f49..8b13789 100644 --- a/image.darknet/src/classifier.h +++ b/image.darknet/src/classifier.h @@ -1,2 +1 @@ -list *read_data_cfg(char *filename); diff --git a/image.darknet/src/coco.c b/image.darknet/src/coco.c deleted file mode 100644 index 8f3c968..0000000 --- a/image.darknet/src/coco.c +++ /dev/null @@ -1,388 +0,0 @@ -#include - -#include "network.h" -#include "detection_layer.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "box.h" -#include "demo.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -char *coco_classes[] = {"person","bicycle","car","motorcycle","airplane","bus","train","truck","boat","traffic light","fire hydrant","stop sign","parking meter","bench","bird","cat","dog","horse","sheep","cow","elephant","bear","zebra","giraffe","backpack","umbrella","handbag","tie","suitcase","frisbee","skis","snowboard","sports ball","kite","baseball bat","baseball glove","skateboard","surfboard","tennis racket","bottle","wine glass","cup","fork","knife","spoon","bowl","banana","apple","sandwich","orange","broccoli","carrot","hot dog","pizza","donut","cake","chair","couch","potted plant","bed","dining table","toilet","tv","laptop","mouse","remote","keyboard","cell phone","microwave","oven","toaster","sink","refrigerator","book","clock","vase","scissors","teddy bear","hair drier","toothbrush"}; - -int coco_ids[] = {1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,27,28,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,70,72,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,90}; - -void train_coco(char *cfgfile, char *weightfile) -{ - //char *train_images = "/home/pjreddie/data/voc/test/train.txt"; - //char *train_images = "/home/pjreddie/data/coco/train.txt"; - char *train_images = "data/coco.trainval.txt"; - //char *train_images = "data/bags.train.list"; - char *backup_directory = "/home/pjreddie/backup/"; - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - int i = *net.seen/imgs; - data train, buffer; - - - layer l = net.layers[net.n - 1]; - - int side = l.side; - int classes = l.classes; - float jitter = l.jitter; - - list *plist = get_paths(train_images); - //int N = plist->size; - char **paths = (char **)list_to_array(plist); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.paths = paths; - args.n = imgs; - args.m = plist->size; - args.classes = classes; - args.jitter = jitter; - args.num_boxes = side; - args.d = &buffer; - args.type = REGION_DATA; - - args.angle = net.angle; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; - - pthread_t load_thread = load_data_in_thread(args); - clock_t time; - //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ - i += 1; - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data_in_thread(args); - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - - /* - image im = float_to_image(net.w, net.h, 3, train.X.vals[113]); - image copy = copy_image(im); - draw_coco(copy, train.y.vals[113], 7, "truth"); - cvWaitKey(0); - free_image(copy); - */ - - time=clock(); - float loss = train_network(net, train); - if (avg_loss < 0) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - - printf("%d: %f, %f avg, %f rate, %lf seconds, %d images\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time), i*imgs); - if(i%1000==0 || (i < 1000 && i%100 == 0)){ - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); - save_weights(net, buff); - } - if(i%100==0){ - char buff[256]; - sprintf(buff, "%s/%s.backup", backup_directory, base); - save_weights(net, buff); - } - free_data(train); - } - char buff[256]; - sprintf(buff, "%s/%s_final.weights", backup_directory, base); - save_weights(net, buff); -} - -void print_cocos(FILE *fp, int image_id, box *boxes, float **probs, int num_boxes, int classes, int w, int h) -{ - int i, j; - for(i = 0; i < num_boxes; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; - - if (xmin < 0) xmin = 0; - if (ymin < 0) ymin = 0; - if (xmax > w) xmax = w; - if (ymax > h) ymax = h; - - float bx = xmin; - float by = ymin; - float bw = xmax - xmin; - float bh = ymax - ymin; - - for(j = 0; j < classes; ++j){ - if (probs[i][j]) fprintf(fp, "{\"image_id\":%d, \"category_id\":%d, \"bbox\":[%f, %f, %f, %f], \"score\":%f},\n", image_id, coco_ids[j], bx, by, bw, bh, probs[i][j]); - } - } -} - -int get_coco_image_id(char *filename) -{ - char *p = strrchr(filename, '_'); - return atoi(p+1); -} - -void validate_coco(char *cfgfile, char *weightfile) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - srand(time(0)); - - char *base = "results/"; - list *plist = get_paths("data/coco_val_5k.list"); - //list *plist = get_paths("/home/pjreddie/data/people-art/test.txt"); - //list *plist = get_paths("/home/pjreddie/data/voc/test/2007_test.txt"); - char **paths = (char **)list_to_array(plist); - - layer l = net.layers[net.n-1]; - int classes = l.classes; - int side = l.side; - - int j; - char buff[1024]; - snprintf(buff, 1024, "%s/coco_results.json", base); - FILE *fp = fopen(buff, "w"); - fprintf(fp, "[\n"); - - box *boxes = calloc(side*side*l.n, sizeof(box)); - float **probs = calloc(side*side*l.n, sizeof(float *)); - for(j = 0; j < side*side*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); - - int m = plist->size; - int i=0; - int t; - - float thresh = .01; - int nms = 1; - float iou_thresh = .5; - - int nthreads = 8; - image *val = calloc(nthreads, sizeof(image)); - image *val_resized = calloc(nthreads, sizeof(image)); - image *buf = calloc(nthreads, sizeof(image)); - image *buf_resized = calloc(nthreads, sizeof(image)); - pthread_t *thr = calloc(nthreads, sizeof(pthread_t)); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.type = IMAGE_DATA; - - for(t = 0; t < nthreads; ++t){ - args.path = paths[i+t]; - args.im = &buf[t]; - args.resized = &buf_resized[t]; - thr[t] = load_data_in_thread(args); - } - time_t start = time(0); - for(i = nthreads; i < m+nthreads; i += nthreads){ - fprintf(stderr, "%d\n", i); - for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ - pthread_join(thr[t], 0); - val[t] = buf[t]; - val_resized[t] = buf_resized[t]; - } - for(t = 0; t < nthreads && i+t < m; ++t){ - args.path = paths[i+t]; - args.im = &buf[t]; - args.resized = &buf_resized[t]; - thr[t] = load_data_in_thread(args); - } - for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ - char *path = paths[i+t-nthreads]; - int image_id = get_coco_image_id(path); - float *X = val_resized[t].data; - network_predict(net, X); - int w = val[t].w; - int h = val[t].h; - get_detection_boxes(l, w, h, thresh, probs, boxes, 0); - if (nms) do_nms_sort(boxes, probs, side*side*l.n, classes, iou_thresh); - print_cocos(fp, image_id, boxes, probs, side*side*l.n, classes, w, h); - free_image(val[t]); - free_image(val_resized[t]); - } - } - fseek(fp, -2, SEEK_CUR); - fprintf(fp, "\n]\n"); - fclose(fp); - - fprintf(stderr, "Total Detection Time: %f Seconds\n", (double)(time(0) - start)); -} - -void validate_coco_recall(char *cfgfile, char *weightfile) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - srand(time(0)); - - char *base = "results/comp4_det_test_"; - list *plist = get_paths("/home/pjreddie/data/voc/test/2007_test.txt"); - char **paths = (char **)list_to_array(plist); - - layer l = net.layers[net.n-1]; - int classes = l.classes; - int side = l.side; - - int j, k; - FILE **fps = calloc(classes, sizeof(FILE *)); - for(j = 0; j < classes; ++j){ - char buff[1024]; - snprintf(buff, 1024, "%s%s.txt", base, coco_classes[j]); - fps[j] = fopen(buff, "w"); - } - box *boxes = calloc(side*side*l.n, sizeof(box)); - float **probs = calloc(side*side*l.n, sizeof(float *)); - for(j = 0; j < side*side*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); - - int m = plist->size; - int i=0; - - float thresh = .001; - int nms = 0; - float iou_thresh = .5; - float nms_thresh = .5; - - int total = 0; - int correct = 0; - int proposals = 0; - float avg_iou = 0; - - for(i = 0; i < m; ++i){ - char *path = paths[i]; - image orig = load_image_color(path, 0, 0); - image sized = resize_image(orig, net.w, net.h); - char *id = basecfg(path); - network_predict(net, sized.data); - get_detection_boxes(l, 1, 1, thresh, probs, boxes, 1); - if (nms) do_nms(boxes, probs, side*side*l.n, 1, nms_thresh); - - char labelpath[4096]; - find_replace(path, "images", "labels", labelpath); - find_replace(labelpath, "JPEGImages", "labels", labelpath); - find_replace(labelpath, ".jpg", ".txt", labelpath); - find_replace(labelpath, ".JPEG", ".txt", labelpath); - - int num_labels = 0; - box_label *truth = read_boxes(labelpath, &num_labels); - for(k = 0; k < side*side*l.n; ++k){ - if(probs[k][0] > thresh){ - ++proposals; - } - } - for (j = 0; j < num_labels; ++j) { - ++total; - box t = {truth[j].x, truth[j].y, truth[j].w, truth[j].h}; - float best_iou = 0; - for(k = 0; k < side*side*l.n; ++k){ - float iou = box_iou(boxes[k], t); - if(probs[k][0] > thresh && iou > best_iou){ - best_iou = iou; - } - } - avg_iou += best_iou; - if(best_iou > iou_thresh){ - ++correct; - } - } - - fprintf(stderr, "%5d %5d %5d\tRPs/Img: %.2f\tIOU: %.2f%%\tRecall:%.2f%%\n", i, correct, total, (float)proposals/(i+1), avg_iou*100/total, 100.*correct/total); - free(id); - free_image(orig); - free_image(sized); - } -} - -void test_coco(char *cfgfile, char *weightfile, char *filename, float thresh) -{ - image **alphabet = load_alphabet(); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - detection_layer l = net.layers[net.n-1]; - set_batch_network(&net, 1); - srand(2222222); - float nms = .4; - clock_t time; - char buff[256]; - char *input = buff; - int j; - box *boxes = calloc(l.side*l.side*l.n, sizeof(box)); - float **probs = calloc(l.side*l.side*l.n, sizeof(float *)); - for(j = 0; j < l.side*l.side*l.n; ++j) probs[j] = calloc(l.classes, sizeof(float *)); - 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, "\n"); - } - image im = load_image_color(input,0,0); - image sized = resize_image(im, net.w, net.h); - float *X = sized.data; - time=clock(); - network_predict(net, X); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - get_detection_boxes(l, 1, 1, thresh, probs, boxes, 0); - if (nms) do_nms_sort(boxes, probs, l.side*l.side*l.n, l.classes, nms); - draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, coco_classes, alphabet, 80); - save_image(im, "prediction"); - show_image(im, "predictions"); - free_image(im); - free_image(sized); -#ifdef OPENCV - cvWaitKey(0); - cvDestroyAllWindows(); -#endif - if (filename) break; - } -} - -void run_coco(int argc, char **argv) -{ - char *prefix = find_char_arg(argc, argv, "-prefix", 0); - float thresh = find_float_arg(argc, argv, "-thresh", .2); - int cam_index = find_int_arg(argc, argv, "-c", 0); - int frame_skip = find_int_arg(argc, argv, "-s", 0); - - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *filename = (argc > 5) ? argv[5]: 0; - if(0==strcmp(argv[2], "test")) test_coco(cfg, weights, filename, thresh); - else if(0==strcmp(argv[2], "train")) train_coco(cfg, weights); - else if(0==strcmp(argv[2], "valid")) validate_coco(cfg, weights); - else if(0==strcmp(argv[2], "recall")) validate_coco_recall(cfg, weights); - else if(0==strcmp(argv[2], "demo")) demo(cfg, weights, thresh, cam_index, filename, coco_classes, 80, frame_skip, prefix, .5); -} diff --git a/image.darknet/src/col2im.h b/image.darknet/src/col2im.h index 0237497..3fbe053 100644 --- a/image.darknet/src/col2im.h +++ b/image.darknet/src/col2im.h @@ -6,7 +6,7 @@ void col2im_cpu(float* data_col, int ksize, int stride, int pad, float* data_im); #ifdef GPU -void col2im_ongpu(float *data_col, +void col2im_gpu(float *data_col, int channels, int height, int width, int ksize, int stride, int pad, float *data_im); #endif diff --git a/image.darknet/src/col2im_kernels.cu b/image.darknet/src/col2im_kernels.cu index aed2df9..ba45e0f 100644 --- a/image.darknet/src/col2im_kernels.cu +++ b/image.darknet/src/col2im_kernels.cu @@ -41,7 +41,7 @@ __global__ void col2im_gpu_kernel(const int n, const float* data_col, } } -void col2im_ongpu(float *data_col, +void col2im_gpu(float *data_col, int channels, int height, int width, int ksize, int stride, int pad, float *data_im){ // We are going to launch channels * height_col * width_col kernels, each diff --git a/image.darknet/src/compare.c b/image.darknet/src/compare.c index 4fd266c..d2d2b3b 100644 --- a/image.darknet/src/compare.c +++ b/image.darknet/src/compare.c @@ -54,7 +54,7 @@ void train_compare(char *cfgfile, char *weightfile) float loss = train_network(net, train); if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - printf("%.3f: %f, %f avg, %lf seconds, %d images\n", (float)*net.seen/N, loss, avg_loss, sec(clock()-time), *net.seen); + printf("%.3f: %f, %f avg, %lf seconds, %ld images\n", (float)*net.seen/N, loss, avg_loss, sec(clock()-time), *net.seen); free_data(train); if(i%100 == 0){ char buff[256]; diff --git a/image.darknet/src/connected_layer.c b/image.darknet/src/connected_layer.c index b678ed0..353f4e5 100644 --- a/image.darknet/src/connected_layer.c +++ b/image.darknet/src/connected_layer.c @@ -1,4 +1,5 @@ #include "connected_layer.h" +#include "convolutional_layer.h" #include "batchnorm_layer.h" #include "utils.h" #include "cuda.h" @@ -10,10 +11,11 @@ #include #include -connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVATION activation, int batch_normalize) +layer make_connected_layer(int batch, int inputs, int outputs, ACTIVATION activation, int batch_normalize, int adam) { int i; - connected_layer l = {0}; + layer l = {0}; + l.learning_rate_scale = 1; l.type = CONNECTED; l.inputs = inputs; @@ -50,6 +52,14 @@ connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVAT l.biases[i] = 0; } + if(adam){ + l.m = calloc(l.inputs*l.outputs, sizeof(float)); + l.v = calloc(l.inputs*l.outputs, sizeof(float)); + l.bias_m = calloc(l.outputs, sizeof(float)); + l.scale_m = calloc(l.outputs, sizeof(float)); + l.bias_v = calloc(l.outputs, sizeof(float)); + l.scale_v = calloc(l.outputs, sizeof(float)); + } if(batch_normalize){ l.scales = calloc(outputs, sizeof(float)); l.scale_updates = calloc(outputs, sizeof(float)); @@ -82,10 +92,16 @@ connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVAT l.output_gpu = cuda_make_array(l.output, outputs*batch); l.delta_gpu = cuda_make_array(l.delta, outputs*batch); - if(batch_normalize){ - l.scales_gpu = cuda_make_array(l.scales, outputs); - l.scale_updates_gpu = cuda_make_array(l.scale_updates, outputs); + if (adam) { + l.m_gpu = cuda_make_array(0, inputs*outputs); + l.v_gpu = cuda_make_array(0, inputs*outputs); + l.bias_m_gpu = cuda_make_array(0, outputs); + l.bias_v_gpu = cuda_make_array(0, outputs); + l.scale_m_gpu = cuda_make_array(0, outputs); + l.scale_v_gpu = cuda_make_array(0, outputs); + } + if(batch_normalize){ l.mean_gpu = cuda_make_array(l.mean, outputs); l.variance_gpu = cuda_make_array(l.variance, outputs); @@ -95,8 +111,17 @@ connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVAT l.mean_delta_gpu = cuda_make_array(l.mean, outputs); l.variance_delta_gpu = cuda_make_array(l.variance, outputs); + l.scales_gpu = cuda_make_array(l.scales, outputs); + l.scale_updates_gpu = cuda_make_array(l.scale_updates, outputs); + l.x_gpu = cuda_make_array(l.output, l.batch*outputs); l.x_norm_gpu = cuda_make_array(l.output, l.batch*outputs); +#ifdef CUDNN + cudnnCreateTensorDescriptor(&l.normTensorDesc); + cudnnCreateTensorDescriptor(&l.dstTensorDesc); + cudnnSetTensor4dDescriptor(l.dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l.batch, l.out_c, l.out_h, l.out_w); + cudnnSetTensor4dDescriptor(l.normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l.out_c, 1, 1); +#endif } #endif l.activation = activation; @@ -104,8 +129,12 @@ connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVAT return l; } -void update_connected_layer(connected_layer l, int batch, float learning_rate, float momentum, float decay) +void update_connected_layer(layer l, update_args a) { + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; axpy_cpu(l.outputs, learning_rate/batch, l.bias_updates, 1, l.biases, 1); scal_cpu(l.outputs, momentum, l.bias_updates, 1); @@ -119,63 +148,39 @@ void update_connected_layer(connected_layer l, int batch, float learning_rate, f scal_cpu(l.inputs*l.outputs, momentum, l.weight_updates, 1); } -void forward_connected_layer(connected_layer l, network_state state) +void forward_connected_layer(layer l, network net) { - int i; fill_cpu(l.outputs*l.batch, 0, l.output, 1); int m = l.batch; int k = l.inputs; int n = l.outputs; - float *a = state.input; + float *a = net.input; float *b = l.weights; float *c = l.output; gemm(0,1,m,n,k,1,a,k,b,k,1,c,n); if(l.batch_normalize){ - if(state.train){ - mean_cpu(l.output, l.batch, l.outputs, 1, l.mean); - variance_cpu(l.output, l.mean, l.batch, l.outputs, 1, l.variance); - - scal_cpu(l.outputs, .95, l.rolling_mean, 1); - axpy_cpu(l.outputs, .05, l.mean, 1, l.rolling_mean, 1); - scal_cpu(l.outputs, .95, l.rolling_variance, 1); - axpy_cpu(l.outputs, .05, l.variance, 1, l.rolling_variance, 1); - - copy_cpu(l.outputs*l.batch, l.output, 1, l.x, 1); - normalize_cpu(l.output, l.mean, l.variance, l.batch, l.outputs, 1); - copy_cpu(l.outputs*l.batch, l.output, 1, l.x_norm, 1); - } else { - normalize_cpu(l.output, l.rolling_mean, l.rolling_variance, l.batch, l.outputs, 1); - } - scale_bias(l.output, l.scales, l.batch, l.outputs, 1); - } - for(i = 0; i < l.batch; ++i){ - axpy_cpu(l.outputs, 1, l.biases, 1, l.output + i*l.outputs, 1); + forward_batchnorm_layer(l, net); + } else { + add_bias(l.output, l.biases, l.batch, l.outputs, 1); } activate_array(l.output, l.outputs*l.batch, l.activation); } -void backward_connected_layer(connected_layer l, network_state state) +void backward_connected_layer(layer l, network net) { - int i; gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); - for(i = 0; i < l.batch; ++i){ - axpy_cpu(l.outputs, 1, l.delta + i*l.outputs, 1, l.bias_updates, 1); - } - if(l.batch_normalize){ - backward_scale_cpu(l.x_norm, l.delta, l.batch, l.outputs, 1, l.scale_updates); - - scale_bias(l.delta, l.scales, l.batch, l.outputs, 1); - mean_delta_cpu(l.delta, l.variance, l.batch, l.outputs, 1, l.mean_delta); - variance_delta_cpu(l.x, l.delta, l.mean, l.variance, l.batch, l.outputs, 1, l.variance_delta); - normalize_delta_cpu(l.x, l.mean, l.variance, l.mean_delta, l.variance_delta, l.batch, l.outputs, 1, l.delta); + if(l.batch_normalize){ + backward_batchnorm_layer(l, net); + } else { + backward_bias(l.bias_updates, l.delta, l.batch, l.outputs, 1); } int m = l.outputs; int k = l.batch; int n = l.inputs; float *a = l.delta; - float *b = state.input; + float *b = net.input; float *c = l.weight_updates; gemm(1,0,m,n,k,1,a,m,b,n,1,c,n); @@ -185,7 +190,7 @@ void backward_connected_layer(connected_layer l, network_state state) a = l.delta; b = l.weights; - c = state.delta; + c = net.delta; if(c) gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); } @@ -213,11 +218,11 @@ void statistics_connected_layer(layer l) printf("Scales "); print_statistics(l.scales, l.outputs); /* - printf("Rolling Mean "); - print_statistics(l.rolling_mean, l.outputs); - printf("Rolling Variance "); - print_statistics(l.rolling_variance, l.outputs); - */ + printf("Rolling Mean "); + print_statistics(l.rolling_mean, l.outputs); + printf("Rolling Variance "); + print_statistics(l.rolling_variance, l.outputs); + */ } printf("Biases "); print_statistics(l.biases, l.outputs); @@ -227,7 +232,7 @@ void statistics_connected_layer(layer l) #ifdef GPU -void pull_connected_layer(connected_layer l) +void pull_connected_layer(layer l) { cuda_pull_array(l.weights_gpu, l.weights, l.inputs*l.outputs); cuda_pull_array(l.biases_gpu, l.biases, l.outputs); @@ -240,7 +245,7 @@ void pull_connected_layer(connected_layer l) } } -void push_connected_layer(connected_layer l) +void push_connected_layer(layer l) { cuda_push_array(l.weights_gpu, l.weights, l.inputs*l.outputs); cuda_push_array(l.biases_gpu, l.biases, l.outputs); @@ -253,62 +258,70 @@ void push_connected_layer(connected_layer l) } } -void update_connected_layer_gpu(connected_layer l, int batch, float learning_rate, float momentum, float decay) +void update_connected_layer_gpu(layer l, update_args a) { - axpy_ongpu(l.outputs, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); - scal_ongpu(l.outputs, momentum, l.bias_updates_gpu, 1); + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + if(a.adam){ + adam_update_gpu(l.weights_gpu, l.weight_updates_gpu, l.m_gpu, l.v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.inputs*l.outputs, batch, a.t); + adam_update_gpu(l.biases_gpu, l.bias_updates_gpu, l.bias_m_gpu, l.bias_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.outputs, batch, a.t); + if(l.scales_gpu){ + adam_update_gpu(l.scales_gpu, l.scale_updates_gpu, l.scale_m_gpu, l.scale_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.outputs, batch, a.t); + } + }else{ + axpy_gpu(l.outputs, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); + scal_gpu(l.outputs, momentum, l.bias_updates_gpu, 1); - if(l.batch_normalize){ - axpy_ongpu(l.outputs, learning_rate/batch, l.scale_updates_gpu, 1, l.scales_gpu, 1); - scal_ongpu(l.outputs, momentum, l.scale_updates_gpu, 1); - } + if(l.batch_normalize){ + axpy_gpu(l.outputs, learning_rate/batch, l.scale_updates_gpu, 1, l.scales_gpu, 1); + scal_gpu(l.outputs, momentum, l.scale_updates_gpu, 1); + } - axpy_ongpu(l.inputs*l.outputs, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); - axpy_ongpu(l.inputs*l.outputs, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); - scal_ongpu(l.inputs*l.outputs, momentum, l.weight_updates_gpu, 1); + axpy_gpu(l.inputs*l.outputs, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); + axpy_gpu(l.inputs*l.outputs, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); + scal_gpu(l.inputs*l.outputs, momentum, l.weight_updates_gpu, 1); + } } -void forward_connected_layer_gpu(connected_layer l, network_state state) +void forward_connected_layer_gpu(layer l, network net) { - int i; - fill_ongpu(l.outputs*l.batch, 0, l.output_gpu, 1); + fill_gpu(l.outputs*l.batch, 0, l.output_gpu, 1); int m = l.batch; int k = l.inputs; int n = l.outputs; - float * a = state.input; + float * a = net.input_gpu; float * b = l.weights_gpu; float * c = l.output_gpu; - gemm_ongpu(0,1,m,n,k,1,a,k,b,k,1,c,n); - if(l.batch_normalize){ - forward_batchnorm_layer_gpu(l, state); - } - for(i = 0; i < l.batch; ++i){ - axpy_ongpu(l.outputs, 1, l.biases_gpu, 1, l.output_gpu + i*l.outputs, 1); + gemm_gpu(0,1,m,n,k,1,a,k,b,k,1,c,n); + + if (l.batch_normalize) { + forward_batchnorm_layer_gpu(l, net); + } else { + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.outputs, 1); } - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); } -void backward_connected_layer_gpu(connected_layer l, network_state state) +void backward_connected_layer_gpu(layer l, network net) { - int i; - constrain_ongpu(l.outputs*l.batch, 1, l.delta_gpu, 1); - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); - for(i = 0; i < l.batch; ++i){ - axpy_ongpu(l.outputs, 1, l.delta_gpu + i*l.outputs, 1, l.bias_updates_gpu, 1); - } - + constrain_gpu(l.outputs*l.batch, 1, l.delta_gpu, 1); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); if(l.batch_normalize){ - backward_batchnorm_layer_gpu(l, state); + backward_batchnorm_layer_gpu(l, net); + } else { + backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.outputs, 1); } int m = l.outputs; int k = l.batch; int n = l.inputs; float * a = l.delta_gpu; - float * b = state.input; + float * b = net.input_gpu; float * c = l.weight_updates_gpu; - gemm_ongpu(1,0,m,n,k,1,a,m,b,n,1,c,n); + gemm_gpu(1,0,m,n,k,1,a,m,b,n,1,c,n); m = l.batch; k = l.outputs; @@ -316,8 +329,8 @@ void backward_connected_layer_gpu(connected_layer l, network_state state) a = l.delta_gpu; b = l.weights_gpu; - c = state.delta; + c = net.delta_gpu; - if(c) gemm_ongpu(0,0,m,n,k,1,a,k,b,n,1,c,n); + if(c) gemm_gpu(0,0,m,n,k,1,a,k,b,n,1,c,n); } #endif diff --git a/image.darknet/src/connected_layer.h b/image.darknet/src/connected_layer.h index 23797b1..6727a96 100644 --- a/image.darknet/src/connected_layer.h +++ b/image.darknet/src/connected_layer.h @@ -5,22 +5,18 @@ #include "layer.h" #include "network.h" -typedef layer connected_layer; +layer make_connected_layer(int batch, int inputs, int outputs, ACTIVATION activation, int batch_normalize, int adam); -connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVATION activation, int batch_normalize); - -void forward_connected_layer(connected_layer layer, network_state state); -void backward_connected_layer(connected_layer layer, network_state state); -void update_connected_layer(connected_layer layer, int batch, float learning_rate, float momentum, float decay); -void denormalize_connected_layer(layer l); -void statistics_connected_layer(layer l); +void forward_connected_layer(layer l, network net); +void backward_connected_layer(layer l, network net); +void update_connected_layer(layer l, update_args a); #ifdef GPU -void forward_connected_layer_gpu(connected_layer layer, network_state state); -void backward_connected_layer_gpu(connected_layer layer, network_state state); -void update_connected_layer_gpu(connected_layer layer, int batch, float learning_rate, float momentum, float decay); -void push_connected_layer(connected_layer layer); -void pull_connected_layer(connected_layer layer); +void forward_connected_layer_gpu(layer l, network net); +void backward_connected_layer_gpu(layer l, network net); +void update_connected_layer_gpu(layer l, update_args a); +void push_connected_layer(layer l); +void pull_connected_layer(layer l); #endif #endif diff --git a/image.darknet/src/convolutional_kernels.cu b/image.darknet/src/convolutional_kernels.cu index fcaea03..4a1047b 100644 --- a/image.darknet/src/convolutional_kernels.cu +++ b/image.darknet/src/convolutional_kernels.cu @@ -33,7 +33,7 @@ __global__ void binarize_input_kernel(float *input, int n, int size, float *bina int i = 0; float mean = 0; for(i = 0; i < n; ++i){ - mean += abs(input[i*size + s]); + mean += fabsf(input[i*size + s]); } mean = mean / n; for(i = 0; i < n; ++i){ @@ -55,7 +55,7 @@ __global__ void binarize_weights_kernel(float *weights, int n, int size, float * int i = 0; float mean = 0; for(i = 0; i < size; ++i){ - mean += abs(weights[f*size + i]); + mean += fabsf(weights[f*size + i]); } mean = mean / size; for(i = 0; i < size; ++i){ @@ -70,19 +70,19 @@ void binarize_weights_gpu(float *weights, int n, int size, float *binary) check_error(cudaPeekAtLastError()); } -void forward_convolutional_layer_gpu(convolutional_layer l, network_state state) +void forward_convolutional_layer_gpu(convolutional_layer l, network net) { - fill_ongpu(l.outputs*l.batch, 0, l.output_gpu, 1); + fill_gpu(l.outputs*l.batch, 0, l.output_gpu, 1); if(l.binary){ - binarize_weights_gpu(l.weights_gpu, l.n, l.c*l.size*l.size, l.binary_weights_gpu); + binarize_weights_gpu(l.weights_gpu, l.n, l.c/l.groups*l.size*l.size, l.binary_weights_gpu); swap_binary(&l); } if(l.xnor){ - binarize_weights_gpu(l.weights_gpu, l.n, l.c*l.size*l.size, l.binary_weights_gpu); + binarize_weights_gpu(l.weights_gpu, l.n, l.c/l.groups*l.size*l.size, l.binary_weights_gpu); swap_binary(&l); - binarize_gpu(state.input, l.c*l.h*l.w*l.batch, l.binary_input_gpu); - state.input = l.binary_input_gpu; + binarize_gpu(net.input_gpu, l.c*l.h*l.w*l.batch, l.binary_input_gpu); + net.input_gpu = l.binary_input_gpu; } #ifdef CUDNN @@ -90,74 +90,126 @@ void forward_convolutional_layer_gpu(convolutional_layer l, network_state state) cudnnConvolutionForward(cudnn_handle(), &one, l.srcTensorDesc, - state.input, + net.input_gpu, l.weightDesc, l.weights_gpu, l.convDesc, l.fw_algo, - state.workspace, + net.workspace, l.workspace_size, &one, l.dstTensorDesc, l.output_gpu); #else - int i; - int m = l.n; - int k = l.size*l.size*l.c; + int i, j; + int m = l.n/l.groups; + int k = l.size*l.size*l.c/l.groups; int n = l.out_w*l.out_h; for(i = 0; i < l.batch; ++i){ - im2col_ongpu(state.input + i*l.c*l.h*l.w, l.c, l.h, l.w, l.size, l.stride, l.pad, state.workspace); - float * a = l.weights_gpu; - float * b = state.workspace; - float * c = l.output_gpu; - gemm_ongpu(0,0,m,n,k,1.,a,k,b,n,1.,c+i*m*n,n); + for(j = 0; j < l.groups; ++j){ + float *a = l.weights_gpu + j*l.nweights/l.groups; + float *b = net.workspace; + float *c = l.output_gpu + (i*l.groups + j)*n*m; + float *im = net.input_gpu + (i*l.groups + j)*l.c/l.groups*l.h*l.w; + + if (l.size == 1){ + b = im; + } else { + im2col_gpu(im, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, b); + } + gemm_gpu(0,0,m,n,k,1,a,k,b,n,1,c,n); + } } #endif if (l.batch_normalize) { - forward_batchnorm_layer_gpu(l, state); + forward_batchnorm_layer_gpu(l, net); + } else { + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.n, l.out_w*l.out_h); } - add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.n, l.out_w*l.out_h); - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); //if(l.dot > 0) dot_error_gpu(l); if(l.binary || l.xnor) swap_binary(&l); } -void backward_convolutional_layer_gpu(convolutional_layer l, network_state state) +__global__ void smooth_kernel(float *x, int n, int w, int h, int c, int size, float rate, float *delta) +{ + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(id >= n) return; + + int j = id % w; + id /= w; + int i = id % h; + id /= h; + int k = id % c; + id /= c; + int b = id; + + int w_offset = -(size/2.f); + int h_offset = -(size/2.f); + + int out_index = j + w*(i + h*(k + c*b)); + int l, m; + for(l = 0; l < size; ++l){ + for(m = 0; m < size; ++m){ + int cur_h = h_offset + i + l; + int cur_w = w_offset + j + m; + int index = cur_w + w*(cur_h + h*(k + b*c)); + int valid = (cur_h >= 0 && cur_h < h && + cur_w >= 0 && cur_w < w); + delta[out_index] += valid ? rate*(x[index] - x[out_index]) : 0; + } + } +} + +extern "C" void smooth_layer(layer l, int size, float rate) { - //constrain_ongpu(l.outputs*l.batch, 1, l.delta_gpu, 1); - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + int h = l.out_h; + int w = l.out_w; + int c = l.out_c; + + size_t n = h*w*c*l.batch; + + smooth_kernel<<>>(l.output_gpu, n, l.w, l.h, l.c, size, rate, l.delta_gpu); + check_error(cudaPeekAtLastError()); +} + +void backward_convolutional_layer_gpu(convolutional_layer l, network net) +{ + if(l.smooth){ + smooth_layer(l, 5, l.smooth); + } + //constrain_gpu(l.outputs*l.batch, 1, l.delta_gpu, 1); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); - backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.n, l.out_w*l.out_h); if(l.batch_normalize){ - backward_batchnorm_layer_gpu(l, state); - //axpy_ongpu(l.outputs*l.batch, -state.net.decay, l.x_gpu, 1, l.delta_gpu, 1); + backward_batchnorm_layer_gpu(l, net); } else { - //axpy_ongpu(l.outputs*l.batch, -state.net.decay, l.output_gpu, 1, l.delta_gpu, 1); + backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.n, l.out_w*l.out_h); } - float *original_input = state.input; + float *original_input = net.input_gpu; - if(l.xnor) state.input = l.binary_input_gpu; + if(l.xnor) net.input_gpu = l.binary_input_gpu; #ifdef CUDNN float one = 1; cudnnConvolutionBackwardFilter(cudnn_handle(), &one, l.srcTensorDesc, - state.input, + net.input_gpu, l.ddstTensorDesc, l.delta_gpu, l.convDesc, l.bf_algo, - state.workspace, + net.workspace, l.workspace_size, &one, l.dweightDesc, l.weight_updates_gpu); - if(state.delta){ + if(net.delta_gpu){ if(l.binary || l.xnor) swap_binary(&l); cudnnConvolutionBackwardData(cudnn_handle(), &one, @@ -167,108 +219,111 @@ void backward_convolutional_layer_gpu(convolutional_layer l, network_state state l.delta_gpu, l.convDesc, l.bd_algo, - state.workspace, + net.workspace, l.workspace_size, &one, l.dsrcTensorDesc, - state.delta); + net.delta_gpu); if(l.binary || l.xnor) swap_binary(&l); - if(l.xnor) gradient_array_ongpu(original_input, l.batch*l.c*l.h*l.w, HARDTAN, state.delta); + if(l.xnor) gradient_array_gpu(original_input, l.batch*l.c*l.h*l.w, HARDTAN, net.delta_gpu); } #else - int m = l.n; - int n = l.size*l.size*l.c; + int m = l.n/l.groups; + int n = l.size*l.size*l.c/l.groups; int k = l.out_w*l.out_h; - int i; + int i, j; for(i = 0; i < l.batch; ++i){ - float * a = l.delta_gpu; - float * b = state.workspace; - float * c = l.weight_updates_gpu; - - im2col_ongpu(state.input + i*l.c*l.h*l.w, l.c, l.h, l.w, l.size, l.stride, l.pad, state.workspace); - gemm_ongpu(0,1,m,n,k,1,a + i*m*k,k,b,k,1,c,n); - - if(state.delta){ - if(l.binary || l.xnor) swap_binary(&l); - float * a = l.weights_gpu; - float * b = l.delta_gpu; - float * c = state.workspace; - - gemm_ongpu(1,0,n,k,m,1,a,n,b + i*k*m,k,0,c,k); - - col2im_ongpu(state.workspace, l.c, l.h, l.w, l.size, l.stride, l.pad, state.delta + i*l.c*l.h*l.w); - if(l.binary || l.xnor) { - swap_binary(&l); + for(j = 0; j < l.groups; ++j){ + float *a = l.delta_gpu + (i*l.groups + j)*m*k; + float *b = net.workspace; + float *c = l.weight_updates_gpu + j*l.nweights/l.groups; + + float *im = net.input_gpu+(i*l.groups + j)*l.c/l.groups*l.h*l.w; + float *imd = net.delta_gpu+(i*l.groups + j)*l.c/l.groups*l.h*l.w; + + im2col_gpu(im, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, b); + gemm_gpu(0,1,m,n,k,1,a,k,b,k,1,c,n); + + if (net.delta_gpu) { + if (l.binary || l.xnor) swap_binary(&l); + a = l.weights_gpu + j*l.nweights/l.groups; + b = l.delta_gpu + (i*l.groups + j)*m*k; + c = net.workspace; + if (l.size == 1) { + c = imd; + } + + gemm_gpu(1,0,n,k,m,1,a,n,b,k,0,c,k); + + if (l.size != 1) { + col2im_gpu(net.workspace, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, imd); + } + if(l.binary || l.xnor) { + swap_binary(&l); + } } - if(l.xnor) gradient_array_ongpu(original_input + i*l.c*l.h*l.w, l.c*l.h*l.w, HARDTAN, state.delta + i*l.c*l.h*l.w); + if(l.xnor) gradient_array_gpu(original_input + i*l.c*l.h*l.w, l.c*l.h*l.w, HARDTAN, net.delta_gpu + i*l.c*l.h*l.w); } } #endif } -void pull_convolutional_layer(convolutional_layer layer) +void pull_convolutional_layer(layer l) { - cuda_pull_array(layer.weights_gpu, layer.weights, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.biases_gpu, layer.biases, layer.n); - cuda_pull_array(layer.weight_updates_gpu, layer.weight_updates, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.bias_updates_gpu, layer.bias_updates, layer.n); - if (layer.batch_normalize){ - cuda_pull_array(layer.scales_gpu, layer.scales, layer.n); - cuda_pull_array(layer.rolling_mean_gpu, layer.rolling_mean, layer.n); - cuda_pull_array(layer.rolling_variance_gpu, layer.rolling_variance, layer.n); - } - if (layer.adam){ - cuda_pull_array(layer.m_gpu, layer.m, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.v_gpu, layer.v, layer.c*layer.n*layer.size*layer.size); + cuda_pull_array(l.weights_gpu, l.weights, l.nweights); + cuda_pull_array(l.biases_gpu, l.biases, l.n); + cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.nweights); + cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.n); + if (l.batch_normalize){ + cuda_pull_array(l.scales_gpu, l.scales, l.n); + cuda_pull_array(l.rolling_mean_gpu, l.rolling_mean, l.n); + cuda_pull_array(l.rolling_variance_gpu, l.rolling_variance, l.n); } } -void push_convolutional_layer(convolutional_layer layer) +void push_convolutional_layer(layer l) { - cuda_push_array(layer.weights_gpu, layer.weights, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.biases_gpu, layer.biases, layer.n); - cuda_push_array(layer.weight_updates_gpu, layer.weight_updates, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.bias_updates_gpu, layer.bias_updates, layer.n); - if (layer.batch_normalize){ - cuda_push_array(layer.scales_gpu, layer.scales, layer.n); - cuda_push_array(layer.rolling_mean_gpu, layer.rolling_mean, layer.n); - cuda_push_array(layer.rolling_variance_gpu, layer.rolling_variance, layer.n); - } - if (layer.adam){ - cuda_push_array(layer.m_gpu, layer.m, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.v_gpu, layer.v, layer.c*layer.n*layer.size*layer.size); + cuda_push_array(l.weights_gpu, l.weights, l.nweights); + cuda_push_array(l.biases_gpu, l.biases, l.n); + cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.nweights); + cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.n); + if (l.batch_normalize){ + cuda_push_array(l.scales_gpu, l.scales, l.n); + cuda_push_array(l.rolling_mean_gpu, l.rolling_mean, l.n); + cuda_push_array(l.rolling_variance_gpu, l.rolling_variance, l.n); } } -void update_convolutional_layer_gpu(convolutional_layer layer, int batch, float learning_rate, float momentum, float decay) +void update_convolutional_layer_gpu(layer l, update_args a) { - int size = layer.size*layer.size*layer.c*layer.n; - axpy_ongpu(layer.n, learning_rate/batch, layer.bias_updates_gpu, 1, layer.biases_gpu, 1); - scal_ongpu(layer.n, momentum, layer.bias_updates_gpu, 1); - - if(layer.scales_gpu){ - axpy_ongpu(layer.n, learning_rate/batch, layer.scale_updates_gpu, 1, layer.scales_gpu, 1); - scal_ongpu(layer.n, momentum, layer.scale_updates_gpu, 1); - } - - if(layer.adam){ - scal_ongpu(size, layer.B1, layer.m_gpu, 1); - scal_ongpu(size, layer.B2, layer.v_gpu, 1); - - axpy_ongpu(size, -decay*batch, layer.weights_gpu, 1, layer.weight_updates_gpu, 1); + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + + if(a.adam){ + adam_update_gpu(l.weights_gpu, l.weight_updates_gpu, l.m_gpu, l.v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.nweights, batch, a.t); + adam_update_gpu(l.biases_gpu, l.bias_updates_gpu, l.bias_m_gpu, l.bias_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.n, batch, a.t); + if(l.scales_gpu){ + adam_update_gpu(l.scales_gpu, l.scale_updates_gpu, l.scale_m_gpu, l.scale_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.n, batch, a.t); + } + }else{ + axpy_gpu(l.nweights, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); + axpy_gpu(l.nweights, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); + scal_gpu(l.nweights, momentum, l.weight_updates_gpu, 1); - axpy_ongpu(size, -(1-layer.B1), layer.weight_updates_gpu, 1, layer.m_gpu, 1); - mul_ongpu(size, layer.weight_updates_gpu, 1, layer.weight_updates_gpu, 1); - axpy_ongpu(size, (1-layer.B2), layer.weight_updates_gpu, 1, layer.v_gpu, 1); + axpy_gpu(l.n, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); + scal_gpu(l.n, momentum, l.bias_updates_gpu, 1); - adam_gpu(size, layer.weights_gpu, layer.m_gpu, layer.v_gpu, layer.B1, layer.B2, learning_rate/batch, layer.eps, layer.t+1); - fill_ongpu(size, 0, layer.weight_updates_gpu, 1); - }else{ - axpy_ongpu(size, -decay*batch, layer.weights_gpu, 1, layer.weight_updates_gpu, 1); - axpy_ongpu(size, learning_rate/batch, layer.weight_updates_gpu, 1, layer.weights_gpu, 1); - scal_ongpu(size, momentum, layer.weight_updates_gpu, 1); + if(l.scales_gpu){ + axpy_gpu(l.n, learning_rate/batch, l.scale_updates_gpu, 1, l.scales_gpu, 1); + scal_gpu(l.n, momentum, l.scale_updates_gpu, 1); + } + } + if(l.clip){ + constrain_gpu(l.nweights, l.clip, l.weights_gpu, 1); } } diff --git a/image.darknet/src/convolutional_layer.c b/image.darknet/src/convolutional_layer.c index 37211ab..1fb58b0 100644 --- a/image.darknet/src/convolutional_layer.c +++ b/image.darknet/src/convolutional_layer.c @@ -12,22 +12,17 @@ #include "xnor_layer.h" #endif -#ifndef AI2 -#define AI2 0 -void forward_xnor_layer(layer l, network_state state); -#endif - void swap_binary(convolutional_layer *l) { float *swap = l->weights; l->weights = l->binary_weights; l->binary_weights = swap; - #ifdef GPU +#ifdef GPU swap = l->weights_gpu; l->weights_gpu = l->binary_weights_gpu; l->binary_weights_gpu = swap; - #endif +#endif } void binarize_weights(float *weights, int n, int size, float *binary) @@ -80,23 +75,15 @@ int convolutional_out_width(convolutional_layer l) image get_convolutional_image(convolutional_layer l) { - int h,w,c; - h = convolutional_out_height(l); - w = convolutional_out_width(l); - c = l.n; - return float_to_image(w,h,c,l.output); + return float_to_image(l.out_w,l.out_h,l.out_c,l.output); } image get_convolutional_delta(convolutional_layer l) { - int h,w,c; - h = convolutional_out_height(l); - w = convolutional_out_width(l); - c = l.n; - return float_to_image(w,h,c,l.delta); + return float_to_image(l.out_w,l.out_h,l.out_c,l.delta); } -size_t get_workspace_size(layer l){ +static size_t get_workspace_size(layer l){ #ifdef CUDNN if(gpu_index >= 0){ size_t most = 0; @@ -127,8 +114,8 @@ size_t get_workspace_size(layer l){ if (s > most) most = s; return most; } - #endif - return (size_t)l.out_h*l.out_w*l.size*l.size*l.c*sizeof(float); +#endif + return (size_t)l.out_h*l.out_w*l.size*l.size*l.c/l.groups*sizeof(float); } #ifdef GPU @@ -137,46 +124,62 @@ void cudnn_convolutional_setup(layer *l) { cudnnSetTensor4dDescriptor(l->dsrcTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->c, l->h, l->w); cudnnSetTensor4dDescriptor(l->ddstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->out_c, l->out_h, l->out_w); - cudnnSetFilter4dDescriptor(l->dweightDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, l->n, l->c, l->size, l->size); cudnnSetTensor4dDescriptor(l->srcTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->c, l->h, l->w); cudnnSetTensor4dDescriptor(l->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->out_c, l->out_h, l->out_w); - cudnnSetFilter4dDescriptor(l->weightDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, l->n, l->c, l->size, l->size); + cudnnSetTensor4dDescriptor(l->normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l->out_c, 1, 1); + + cudnnSetFilter4dDescriptor(l->dweightDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, l->n, l->c/l->groups, l->size, l->size); + cudnnSetFilter4dDescriptor(l->weightDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, l->n, l->c/l->groups, l->size, l->size); + #if CUDNN_MAJOR >= 6 + cudnnSetConvolution2dDescriptor(l->convDesc, l->pad, l->pad, l->stride, l->stride, 1, 1, CUDNN_CROSS_CORRELATION, CUDNN_DATA_FLOAT); + #else cudnnSetConvolution2dDescriptor(l->convDesc, l->pad, l->pad, l->stride, l->stride, 1, 1, CUDNN_CROSS_CORRELATION); + #endif + + #if CUDNN_MAJOR >= 7 + cudnnSetConvolutionGroupCount(l->convDesc, l->groups); + #else + if(l->groups > 1){ + error("CUDNN < 7 doesn't support groups, please upgrade!"); + } + #endif + cudnnGetConvolutionForwardAlgorithm(cudnn_handle(), l->srcTensorDesc, l->weightDesc, l->convDesc, l->dstTensorDesc, - CUDNN_CONVOLUTION_FWD_PREFER_FASTEST, - 0, + CUDNN_CONVOLUTION_FWD_SPECIFY_WORKSPACE_LIMIT, + 2000000000, &l->fw_algo); cudnnGetConvolutionBackwardDataAlgorithm(cudnn_handle(), l->weightDesc, l->ddstTensorDesc, l->convDesc, l->dsrcTensorDesc, - CUDNN_CONVOLUTION_BWD_DATA_PREFER_FASTEST, - 0, + CUDNN_CONVOLUTION_BWD_DATA_SPECIFY_WORKSPACE_LIMIT, + 2000000000, &l->bd_algo); cudnnGetConvolutionBackwardFilterAlgorithm(cudnn_handle(), l->srcTensorDesc, l->ddstTensorDesc, l->convDesc, l->dweightDesc, - CUDNN_CONVOLUTION_BWD_FILTER_PREFER_FASTEST, - 0, + CUDNN_CONVOLUTION_BWD_FILTER_SPECIFY_WORKSPACE_LIMIT, + 2000000000, &l->bf_algo); } #endif #endif -convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int binary, int xnor, int adam) +convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int n, int groups, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int binary, int xnor, int adam) { int i; convolutional_layer l = {0}; l.type = CONVOLUTIONAL; + l.groups = groups; l.h = h; l.w = w; l.c = c; @@ -189,17 +192,23 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.pad = padding; l.batch_normalize = batch_normalize; - l.weights = calloc(c*n*size*size, sizeof(float)); - l.weight_updates = calloc(c*n*size*size, sizeof(float)); + l.weights = calloc(c/groups*n*size*size, sizeof(float)); + l.weight_updates = calloc(c/groups*n*size*size, sizeof(float)); l.biases = calloc(n, sizeof(float)); l.bias_updates = calloc(n, sizeof(float)); + l.nweights = c/groups*n*size*size; + l.nbiases = n; + // float scale = 1./sqrt(size*size*c); - float scale = sqrt(2./(size*size*c)); - for(i = 0; i < c*n*size*size; ++i) l.weights[i] = scale*rand_uniform(-1, 1); - int out_h = convolutional_out_height(l); + float scale = sqrt(2./(size*size*c/l.groups)); + //printf("convscale %f\n", scale); + //scale = .02; + //for(i = 0; i < c*n*size*size; ++i) l.weights[i] = scale*rand_uniform(-1, 1); + for(i = 0; i < l.nweights; ++i) l.weights[i] = scale*rand_normal(); int out_w = convolutional_out_width(l); + int out_h = convolutional_out_height(l); l.out_h = out_h; l.out_w = out_w; l.out_c = n; @@ -213,12 +222,12 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.backward = backward_convolutional_layer; l.update = update_convolutional_layer; if(binary){ - l.binary_weights = calloc(c*n*size*size, sizeof(float)); - l.cweights = calloc(c*n*size*size, sizeof(char)); + l.binary_weights = calloc(l.nweights, sizeof(float)); + l.cweights = calloc(l.nweights, sizeof(char)); l.scales = calloc(n, sizeof(float)); } if(xnor){ - l.binary_weights = calloc(c*n*size*size, sizeof(float)); + l.binary_weights = calloc(l.nweights, sizeof(float)); l.binary_input = calloc(l.inputs*l.batch, sizeof(float)); } @@ -241,9 +250,12 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.x_norm = calloc(l.batch*l.outputs, sizeof(float)); } if(adam){ - l.adam = 1; - l.m = calloc(c*n*size*size, sizeof(float)); - l.v = calloc(c*n*size*size, sizeof(float)); + l.m = calloc(l.nweights, sizeof(float)); + l.v = calloc(l.nweights, sizeof(float)); + l.bias_m = calloc(n, sizeof(float)); + l.scale_m = calloc(n, sizeof(float)); + l.bias_v = calloc(n, sizeof(float)); + l.scale_v = calloc(n, sizeof(float)); } #ifdef GPU @@ -253,12 +265,16 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int if(gpu_index >= 0){ if (adam) { - l.m_gpu = cuda_make_array(l.m, c*n*size*size); - l.v_gpu = cuda_make_array(l.v, c*n*size*size); + l.m_gpu = cuda_make_array(l.m, l.nweights); + l.v_gpu = cuda_make_array(l.v, l.nweights); + l.bias_m_gpu = cuda_make_array(l.bias_m, n); + l.bias_v_gpu = cuda_make_array(l.bias_v, n); + l.scale_m_gpu = cuda_make_array(l.scale_m, n); + l.scale_v_gpu = cuda_make_array(l.scale_v, n); } - l.weights_gpu = cuda_make_array(l.weights, c*n*size*size); - l.weight_updates_gpu = cuda_make_array(l.weight_updates, c*n*size*size); + l.weights_gpu = cuda_make_array(l.weights, l.nweights); + l.weight_updates_gpu = cuda_make_array(l.weight_updates, l.nweights); l.biases_gpu = cuda_make_array(l.biases, n); l.bias_updates_gpu = cuda_make_array(l.bias_updates, n); @@ -267,10 +283,10 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.output_gpu = cuda_make_array(l.output, l.batch*out_h*out_w*n); if(binary){ - l.binary_weights_gpu = cuda_make_array(l.weights, c*n*size*size); + l.binary_weights_gpu = cuda_make_array(l.weights, l.nweights); } if(xnor){ - l.binary_weights_gpu = cuda_make_array(l.weights, c*n*size*size); + l.binary_weights_gpu = cuda_make_array(l.weights, l.nweights); l.binary_input_gpu = cuda_make_array(0, l.inputs*l.batch); } @@ -291,6 +307,7 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.x_norm_gpu = cuda_make_array(l.output, l.batch*out_h*out_w*n); } #ifdef CUDNN + cudnnCreateTensorDescriptor(&l.normTensorDesc); cudnnCreateTensorDescriptor(&l.srcTensorDesc); cudnnCreateTensorDescriptor(&l.dstTensorDesc); cudnnCreateFilterDescriptor(&l.weightDesc); @@ -305,7 +322,7 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.workspace_size = get_workspace_size(l); l.activation = activation; - fprintf(stderr, "conv %5d %2d x%2d /%2d %4d x%4d x%4d -> %4d x%4d x%4d\n", n, size, size, stride, w, h, c, l.out_w, l.out_h, l.out_c); + fprintf(stderr, "conv %5d %2d x%2d /%2d %4d x%4d x%4d -> %4d x%4d x%4d %5.3f BFLOPs\n", n, size, size, stride, w, h, c, l.out_w, l.out_h, l.out_c, (2.0 * l.n * l.size*l.size*l.c/l.groups * l.out_h*l.out_w)/1000000000.); return l; } @@ -315,8 +332,8 @@ void denormalize_convolutional_layer(convolutional_layer l) int i, j; for(i = 0; i < l.n; ++i){ float scale = l.scales[i]/sqrt(l.rolling_variance[i] + .00001); - for(j = 0; j < l.c*l.size*l.size; ++j){ - l.weights[i*l.c*l.size*l.size + j] *= scale; + for(j = 0; j < l.c/l.groups*l.size*l.size; ++j){ + l.weights[i*l.c/l.groups*l.size*l.size + j] *= scale; } l.biases[i] -= l.rolling_mean[i] * scale; l.scales[i] = 1; @@ -325,6 +342,7 @@ void denormalize_convolutional_layer(convolutional_layer l) } } +/* void test_convolutional_layer() { convolutional_layer l = make_convolutional_layer(1, 5, 5, 3, 2, 5, 2, 1, LEAKY, 1, 0, 0, 0); @@ -344,10 +362,10 @@ void test_convolutional_layer() 3,3,3,3,3, 3,3,3,3,3, 3,3,3,3,3}; - network_state state = {0}; - state.input = data; - forward_convolutional_layer(l, state); + //net.input = data; + //forward_convolutional_layer(l); } +*/ void resize_convolutional_layer(convolutional_layer *l, int w, int h) { @@ -424,88 +442,106 @@ void backward_bias(float *bias_updates, float *delta, int batch, int n, int size } } -void forward_convolutional_layer(convolutional_layer l, network_state state) +void forward_convolutional_layer(convolutional_layer l, network net) { - int out_h = convolutional_out_height(l); - int out_w = convolutional_out_width(l); - int i; + int i, j; fill_cpu(l.outputs*l.batch, 0, l.output, 1); if(l.xnor){ - binarize_weights(l.weights, l.n, l.c*l.size*l.size, l.binary_weights); + binarize_weights(l.weights, l.n, l.c/l.groups*l.size*l.size, l.binary_weights); swap_binary(&l); - binarize_cpu(state.input, l.c*l.h*l.w*l.batch, l.binary_input); - state.input = l.binary_input; + binarize_cpu(net.input, l.c*l.h*l.w*l.batch, l.binary_input); + net.input = l.binary_input; } - int m = l.n; - int k = l.size*l.size*l.c; - int n = out_h*out_w; - - - float *a = l.weights; - float *b = state.workspace; - float *c = l.output; - + int m = l.n/l.groups; + int k = l.size*l.size*l.c/l.groups; + int n = l.out_w*l.out_h; for(i = 0; i < l.batch; ++i){ - im2col_cpu(state.input, l.c, l.h, l.w, - l.size, l.stride, l.pad, b); - gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); - c += n*m; - state.input += l.c*l.h*l.w; + for(j = 0; j < l.groups; ++j){ + float *a = l.weights + j*l.nweights/l.groups; + float *b = net.workspace; + float *c = l.output + (i*l.groups + j)*n*m; + float *im = net.input + (i*l.groups + j)*l.c/l.groups*l.h*l.w; + + if (l.size == 1) { + b = im; + } else { + im2col_cpu(im, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, b); + } + gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); + } } if(l.batch_normalize){ - forward_batchnorm_layer(l, state); + forward_batchnorm_layer(l, net); + } else { + add_bias(l.output, l.biases, l.batch, l.n, l.out_h*l.out_w); } - add_bias(l.output, l.biases, l.batch, l.n, out_h*out_w); - activate_array(l.output, m*n*l.batch, l.activation); + activate_array(l.output, l.outputs*l.batch, l.activation); if(l.binary || l.xnor) swap_binary(&l); } -void backward_convolutional_layer(convolutional_layer l, network_state state) +void backward_convolutional_layer(convolutional_layer l, network net) { - int i; - int m = l.n; - int n = l.size*l.size*l.c; - int k = convolutional_out_height(l)* - convolutional_out_width(l); + int i, j; + int m = l.n/l.groups; + int n = l.size*l.size*l.c/l.groups; + int k = l.out_w*l.out_h; - gradient_array(l.output, m*k*l.batch, l.activation, l.delta); - backward_bias(l.bias_updates, l.delta, l.batch, l.n, k); + gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); if(l.batch_normalize){ - backward_batchnorm_layer(l, state); + backward_batchnorm_layer(l, net); + } else { + backward_bias(l.bias_updates, l.delta, l.batch, l.n, k); } for(i = 0; i < l.batch; ++i){ - float *a = l.delta + i*m*k; - float *b = state.workspace; - float *c = l.weight_updates; - - float *im = state.input+i*l.c*l.h*l.w; + for(j = 0; j < l.groups; ++j){ + float *a = l.delta + (i*l.groups + j)*m*k; + float *b = net.workspace; + float *c = l.weight_updates + j*l.nweights/l.groups; + + float *im = net.input + (i*l.groups + j)*l.c/l.groups*l.h*l.w; + float *imd = net.delta + (i*l.groups + j)*l.c/l.groups*l.h*l.w; + + if(l.size == 1){ + b = im; + } else { + im2col_cpu(im, l.c/l.groups, l.h, l.w, + l.size, l.stride, l.pad, b); + } - im2col_cpu(im, l.c, l.h, l.w, - l.size, l.stride, l.pad, b); - gemm(0,1,m,n,k,1,a,k,b,k,1,c,n); + gemm(0,1,m,n,k,1,a,k,b,k,1,c,n); - if(state.delta){ - a = l.weights; - b = l.delta + i*m*k; - c = state.workspace; + if (net.delta) { + a = l.weights + j*l.nweights/l.groups; + b = l.delta + (i*l.groups + j)*m*k; + c = net.workspace; + if (l.size == 1) { + c = imd; + } - gemm(1,0,n,k,m,1,a,n,b,k,0,c,k); + gemm(1,0,n,k,m,1,a,n,b,k,0,c,k); - col2im_cpu(state.workspace, l.c, l.h, l.w, l.size, l.stride, l.pad, state.delta+i*l.c*l.h*l.w); + if (l.size != 1) { + col2im_cpu(net.workspace, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, imd); + } + } } } } -void update_convolutional_layer(convolutional_layer l, int batch, float learning_rate, float momentum, float decay) +void update_convolutional_layer(convolutional_layer l, update_args a) { - int size = l.size*l.size*l.c*l.n; + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + axpy_cpu(l.n, learning_rate/batch, l.bias_updates, 1, l.biases, 1); scal_cpu(l.n, momentum, l.bias_updates, 1); @@ -514,9 +550,9 @@ void update_convolutional_layer(convolutional_layer l, int batch, float learning scal_cpu(l.n, momentum, l.scale_updates, 1); } - axpy_cpu(size, -decay*batch, l.weights, 1, l.weight_updates, 1); - axpy_cpu(size, learning_rate/batch, l.weight_updates, 1, l.weights, 1); - scal_cpu(size, momentum, l.weight_updates, 1); + axpy_cpu(l.nweights, -decay*batch, l.weights, 1, l.weight_updates, 1); + axpy_cpu(l.nweights, learning_rate/batch, l.weight_updates, 1, l.weights, 1); + scal_cpu(l.nweights, momentum, l.weight_updates, 1); } @@ -524,7 +560,7 @@ image get_convolutional_weight(convolutional_layer l, int i) { int h = l.size; int w = l.size; - int c = l.c; + int c = l.c/l.groups; return float_to_image(w,h,c,l.weights+i*h*w*c); } @@ -558,8 +594,14 @@ image *get_weights(convolutional_layer l) int i; for(i = 0; i < l.n; ++i){ weights[i] = copy_image(get_convolutional_weight(l, i)); - //normalize_image(weights[i]); + normalize_image(weights[i]); + /* + char buff[256]; + sprintf(buff, "filter%d", i); + save_image(weights[i], buff); + */ } + //error("hey"); return weights; } diff --git a/image.darknet/src/convolutional_layer.h b/image.darknet/src/convolutional_layer.h index 970aa10..6c261f5 100644 --- a/image.darknet/src/convolutional_layer.h +++ b/image.darknet/src/convolutional_layer.h @@ -10,31 +10,31 @@ typedef layer convolutional_layer; #ifdef GPU -void forward_convolutional_layer_gpu(convolutional_layer layer, network_state state); -void backward_convolutional_layer_gpu(convolutional_layer layer, network_state state); -void update_convolutional_layer_gpu(convolutional_layer layer, int batch, float learning_rate, float momentum, float decay); +void forward_convolutional_layer_gpu(convolutional_layer layer, network net); +void backward_convolutional_layer_gpu(convolutional_layer layer, network net); +void update_convolutional_layer_gpu(convolutional_layer layer, update_args a); void push_convolutional_layer(convolutional_layer layer); void pull_convolutional_layer(convolutional_layer layer); void add_bias_gpu(float *output, float *biases, int batch, int n, int size); void backward_bias_gpu(float *bias_updates, float *delta, int batch, int n, int size); +void adam_update_gpu(float *w, float *d, float *m, float *v, float B1, float B2, float eps, float decay, float rate, int n, int batch, int t); #ifdef CUDNN void cudnn_convolutional_setup(layer *l); #endif #endif -convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int binary, int xnor, int adam); -void denormalize_convolutional_layer(convolutional_layer l); +convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int n, int groups, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int binary, int xnor, int adam); void resize_convolutional_layer(convolutional_layer *layer, int w, int h); -void forward_convolutional_layer(const convolutional_layer layer, network_state state); -void update_convolutional_layer(convolutional_layer layer, int batch, float learning_rate, float momentum, float decay); +void forward_convolutional_layer(const convolutional_layer layer, network net); +void update_convolutional_layer(convolutional_layer layer, update_args a); image *visualize_convolutional_layer(convolutional_layer layer, char *window, image *prev_weights); void binarize_weights(float *weights, int n, int size, float *binary); void swap_binary(convolutional_layer *l); void binarize_weights2(float *weights, int n, int size, char *binary, float *scales); -void backward_convolutional_layer(convolutional_layer layer, network_state state); +void backward_convolutional_layer(convolutional_layer layer, network net); void add_bias(float *output, float *biases, int batch, int n, int size); void backward_bias(float *bias_updates, float *delta, int batch, int n, int size); @@ -45,8 +45,6 @@ image get_convolutional_weight(convolutional_layer layer, int i); int convolutional_out_height(convolutional_layer layer); int convolutional_out_width(convolutional_layer layer); -void rescale_weights(convolutional_layer l, float scale, float trans); -void rgbgr_weights(convolutional_layer l); #endif diff --git a/image.darknet/src/cost_layer.c b/image.darknet/src/cost_layer.c index 39d2398..2138ff2 100644 --- a/image.darknet/src/cost_layer.c +++ b/image.darknet/src/cost_layer.c @@ -9,9 +9,12 @@ COST_TYPE get_cost_type(char *s) { + if (strcmp(s, "seg")==0) return SEG; if (strcmp(s, "sse")==0) return SSE; if (strcmp(s, "masked")==0) return MASKED; if (strcmp(s, "smooth")==0) return SMOOTH; + if (strcmp(s, "L1")==0) return L1; + if (strcmp(s, "wgan")==0) return WGAN; fprintf(stderr, "Couldn't find cost type %s, going with SSE\n", s); return SSE; } @@ -19,12 +22,18 @@ COST_TYPE get_cost_type(char *s) char *get_cost_string(COST_TYPE a) { switch(a){ + case SEG: + return "seg"; case SSE: return "sse"; case MASKED: return "masked"; case SMOOTH: return "smooth"; + case L1: + return "L1"; + case WGAN: + return "wgan"; } return "sse"; } @@ -70,26 +79,28 @@ void resize_cost_layer(cost_layer *l, int inputs) #endif } -void forward_cost_layer(cost_layer l, network_state state) +void forward_cost_layer(cost_layer l, network net) { - if (!state.truth) return; + if (!net.truth) return; if(l.cost_type == MASKED){ int i; for(i = 0; i < l.batch*l.inputs; ++i){ - if(state.truth[i] == SECRET_NUM) state.input[i] = SECRET_NUM; + if(net.truth[i] == SECRET_NUM) net.input[i] = SECRET_NUM; } } if(l.cost_type == SMOOTH){ - smooth_l1_cpu(l.batch*l.inputs, state.input, state.truth, l.delta, l.output); + smooth_l1_cpu(l.batch*l.inputs, net.input, net.truth, l.delta, l.output); + }else if(l.cost_type == L1){ + l1_cpu(l.batch*l.inputs, net.input, net.truth, l.delta, l.output); } else { - l2_cpu(l.batch*l.inputs, state.input, state.truth, l.delta, l.output); + l2_cpu(l.batch*l.inputs, net.input, net.truth, l.delta, l.output); } l.cost[0] = sum_array(l.output, l.batch*l.inputs); } -void backward_cost_layer(const cost_layer l, network_state state) +void backward_cost_layer(const cost_layer l, network net) { - axpy_cpu(l.batch*l.inputs, l.scale, l.delta, 1, state.delta, 1); + axpy_cpu(l.batch*l.inputs, l.scale, l.delta, 1, net.delta, 1); } #ifdef GPU @@ -113,17 +124,30 @@ int float_abs_compare (const void * a, const void * b) return (fa > fb) - (fa < fb); } -void forward_cost_layer_gpu(cost_layer l, network_state state) +void forward_cost_layer_gpu(cost_layer l, network net) { - if (!state.truth) return; - if (l.cost_type == MASKED) { - mask_ongpu(l.batch*l.inputs, state.input, SECRET_NUM, state.truth); + if (!net.truth) return; + if(l.smooth){ + scal_gpu(l.batch*l.inputs, (1-l.smooth), net.truth_gpu, 1); + add_gpu(l.batch*l.inputs, l.smooth * 1./l.inputs, net.truth_gpu, 1); } if(l.cost_type == SMOOTH){ - smooth_l1_gpu(l.batch*l.inputs, state.input, state.truth, l.delta_gpu, l.output_gpu); + smooth_l1_gpu(l.batch*l.inputs, net.input_gpu, net.truth_gpu, l.delta_gpu, l.output_gpu); + } else if (l.cost_type == L1){ + l1_gpu(l.batch*l.inputs, net.input_gpu, net.truth_gpu, l.delta_gpu, l.output_gpu); + } else if (l.cost_type == WGAN){ + wgan_gpu(l.batch*l.inputs, net.input_gpu, net.truth_gpu, l.delta_gpu, l.output_gpu); } else { - l2_gpu(l.batch*l.inputs, state.input, state.truth, l.delta_gpu, l.output_gpu); + l2_gpu(l.batch*l.inputs, net.input_gpu, net.truth_gpu, l.delta_gpu, l.output_gpu); + } + + if (l.cost_type == SEG && l.noobject_scale != 1) { + scale_mask_gpu(l.batch*l.inputs, l.delta_gpu, 0, net.truth_gpu, l.noobject_scale); + scale_mask_gpu(l.batch*l.inputs, l.output_gpu, 0, net.truth_gpu, l.noobject_scale); + } + if (l.cost_type == MASKED) { + mask_gpu(l.batch*l.inputs, net.delta_gpu, SECRET_NUM, net.truth_gpu, 0); } if(l.ratio){ @@ -133,16 +157,20 @@ void forward_cost_layer_gpu(cost_layer l, network_state state) float thresh = l.delta[n]; thresh = 0; printf("%f\n", thresh); - supp_ongpu(l.batch*l.inputs, thresh, l.delta_gpu, 1); + supp_gpu(l.batch*l.inputs, thresh, l.delta_gpu, 1); + } + + if(l.thresh){ + supp_gpu(l.batch*l.inputs, l.thresh*1./l.inputs, l.delta_gpu, 1); } cuda_pull_array(l.output_gpu, l.output, l.batch*l.inputs); l.cost[0] = sum_array(l.output, l.batch*l.inputs); } -void backward_cost_layer_gpu(const cost_layer l, network_state state) +void backward_cost_layer_gpu(const cost_layer l, network net) { - axpy_ongpu(l.batch*l.inputs, l.scale, l.delta_gpu, 1, state.delta, 1); + axpy_gpu(l.batch*l.inputs, l.scale, l.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/src/cost_layer.h b/image.darknet/src/cost_layer.h index a692831..ceb64de 100644 --- a/image.darknet/src/cost_layer.h +++ b/image.darknet/src/cost_layer.h @@ -8,13 +8,13 @@ typedef layer cost_layer; COST_TYPE get_cost_type(char *s); char *get_cost_string(COST_TYPE a); cost_layer make_cost_layer(int batch, int inputs, COST_TYPE type, float scale); -void forward_cost_layer(const cost_layer l, network_state state); -void backward_cost_layer(const cost_layer l, network_state state); +void forward_cost_layer(const cost_layer l, network net); +void backward_cost_layer(const cost_layer l, network net); void resize_cost_layer(cost_layer *l, int inputs); #ifdef GPU -void forward_cost_layer_gpu(cost_layer l, network_state state); -void backward_cost_layer_gpu(const cost_layer l, network_state state); +void forward_cost_layer_gpu(cost_layer l, network net); +void backward_cost_layer_gpu(const cost_layer l, network net); #endif #endif diff --git a/image.darknet/src/crnn_layer.c b/image.darknet/src/crnn_layer.c index 5495880..7dd29f6 100644 --- a/image.darknet/src/crnn_layer.c +++ b/image.darknet/src/crnn_layer.c @@ -48,17 +48,17 @@ layer make_crnn_layer(int batch, int h, int w, int c, int hidden_filters, int ou l.input_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_layer) = make_convolutional_layer(batch*steps, h, w, c, hidden_filters, 3, 1, 1, activation, batch_normalize, 0, 0, 0); + *(l.input_layer) = make_convolutional_layer(batch*steps, h, w, c, hidden_filters, 1, 3, 1, 1, activation, batch_normalize, 0, 0, 0); l.input_layer->batch = batch; l.self_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.self_layer) = make_convolutional_layer(batch*steps, h, w, hidden_filters, hidden_filters, 3, 1, 1, activation, batch_normalize, 0, 0, 0); + *(l.self_layer) = make_convolutional_layer(batch*steps, h, w, hidden_filters, hidden_filters, 1, 3, 1, 1, activation, batch_normalize, 0, 0, 0); l.self_layer->batch = batch; l.output_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.output_layer) = make_convolutional_layer(batch*steps, h, w, hidden_filters, output_filters, 3, 1, 1, activation, batch_normalize, 0, 0, 0); + *(l.output_layer) = make_convolutional_layer(batch*steps, h, w, hidden_filters, output_filters, 1, 3, 1, 1, activation, batch_normalize, 0, 0, 0); l.output_layer->batch = batch; l.output = l.output_layer->output; @@ -81,17 +81,17 @@ layer make_crnn_layer(int batch, int h, int w, int c, int hidden_filters, int ou return l; } -void update_crnn_layer(layer l, int batch, float learning_rate, float momentum, float decay) +void update_crnn_layer(layer l, update_args a) { - update_convolutional_layer(*(l.input_layer), batch, learning_rate, momentum, decay); - update_convolutional_layer(*(l.self_layer), batch, learning_rate, momentum, decay); - update_convolutional_layer(*(l.output_layer), batch, learning_rate, momentum, decay); + update_convolutional_layer(*(l.input_layer), a); + update_convolutional_layer(*(l.self_layer), a); + update_convolutional_layer(*(l.output_layer), a); } -void forward_crnn_layer(layer l, network_state state) +void forward_crnn_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -100,17 +100,17 @@ void forward_crnn_layer(layer l, network_state state) fill_cpu(l.outputs * l.batch * l.steps, 0, output_layer.delta, 1); fill_cpu(l.hidden * l.batch * l.steps, 0, self_layer.delta, 1); fill_cpu(l.hidden * l.batch * l.steps, 0, input_layer.delta, 1); - if(state.train) fill_cpu(l.hidden * l.batch, 0, l.state, 1); + if(net.train) fill_cpu(l.hidden * l.batch, 0, l.state, 1); for (i = 0; i < l.steps; ++i) { - s.input = state.input; + s.input = net.input; forward_convolutional_layer(input_layer, s); s.input = l.state; forward_convolutional_layer(self_layer, s); float *old_state = l.state; - if(state.train) l.state += l.hidden*l.batch; + if(net.train) l.state += l.hidden*l.batch; if(l.shortcut){ copy_cpu(l.hidden * l.batch, old_state, 1, l.state, 1); }else{ @@ -122,17 +122,16 @@ void forward_crnn_layer(layer l, network_state state) s.input = l.state; forward_convolutional_layer(output_layer, s); - state.input += l.inputs*l.batch; + net.input += l.inputs*l.batch; increment_layer(&input_layer, 1); increment_layer(&self_layer, 1); increment_layer(&output_layer, 1); } } -void backward_crnn_layer(layer l, network_state state) +void backward_crnn_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -168,8 +167,8 @@ void backward_crnn_layer(layer l, network_state state) copy_cpu(l.hidden*l.batch, self_layer.delta, 1, input_layer.delta, 1); if (i > 0 && l.shortcut) axpy_cpu(l.hidden*l.batch, 1, self_layer.delta, 1, self_layer.delta - l.hidden*l.batch, 1); - s.input = state.input + i*l.inputs*l.batch; - if(state.delta) s.delta = state.delta + i*l.inputs*l.batch; + s.input = net.input + i*l.inputs*l.batch; + if(net.delta) s.delta = net.delta + i*l.inputs*l.batch; else s.delta = 0; backward_convolutional_layer(input_layer, s); @@ -195,58 +194,57 @@ void push_crnn_layer(layer l) push_convolutional_layer(*(l.output_layer)); } -void update_crnn_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay) +void update_crnn_layer_gpu(layer l, update_args a) { - update_convolutional_layer_gpu(*(l.input_layer), batch, learning_rate, momentum, decay); - update_convolutional_layer_gpu(*(l.self_layer), batch, learning_rate, momentum, decay); - update_convolutional_layer_gpu(*(l.output_layer), batch, learning_rate, momentum, decay); + update_convolutional_layer_gpu(*(l.input_layer), a); + update_convolutional_layer_gpu(*(l.self_layer), a); + update_convolutional_layer_gpu(*(l.output_layer), a); } -void forward_crnn_layer_gpu(layer l, network_state state) +void forward_crnn_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); layer output_layer = *(l.output_layer); - fill_ongpu(l.outputs * l.batch * l.steps, 0, output_layer.delta_gpu, 1); - fill_ongpu(l.hidden * l.batch * l.steps, 0, self_layer.delta_gpu, 1); - fill_ongpu(l.hidden * l.batch * l.steps, 0, input_layer.delta_gpu, 1); - if(state.train) fill_ongpu(l.hidden * l.batch, 0, l.state_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, output_layer.delta_gpu, 1); + fill_gpu(l.hidden * l.batch * l.steps, 0, self_layer.delta_gpu, 1); + fill_gpu(l.hidden * l.batch * l.steps, 0, input_layer.delta_gpu, 1); + if(net.train) fill_gpu(l.hidden * l.batch, 0, l.state_gpu, 1); for (i = 0; i < l.steps; ++i) { - s.input = state.input; + s.input_gpu = net.input_gpu; forward_convolutional_layer_gpu(input_layer, s); - s.input = l.state_gpu; + s.input_gpu = l.state_gpu; forward_convolutional_layer_gpu(self_layer, s); float *old_state = l.state_gpu; - if(state.train) l.state_gpu += l.hidden*l.batch; + if(net.train) l.state_gpu += l.hidden*l.batch; if(l.shortcut){ - copy_ongpu(l.hidden * l.batch, old_state, 1, l.state_gpu, 1); + copy_gpu(l.hidden * l.batch, old_state, 1, l.state_gpu, 1); }else{ - fill_ongpu(l.hidden * l.batch, 0, l.state_gpu, 1); + fill_gpu(l.hidden * l.batch, 0, l.state_gpu, 1); } - axpy_ongpu(l.hidden * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); - axpy_ongpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.hidden * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); - s.input = l.state_gpu; + s.input_gpu = l.state_gpu; forward_convolutional_layer_gpu(output_layer, s); - state.input += l.inputs*l.batch; + net.input_gpu += l.inputs*l.batch; increment_layer(&input_layer, 1); increment_layer(&self_layer, 1); increment_layer(&output_layer, 1); } } -void backward_crnn_layer_gpu(layer l, network_state state) +void backward_crnn_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -256,25 +254,25 @@ void backward_crnn_layer_gpu(layer l, network_state state) increment_layer(&output_layer, l.steps - 1); l.state_gpu += l.hidden*l.batch*l.steps; for (i = l.steps-1; i >= 0; --i) { - copy_ongpu(l.hidden * l.batch, input_layer.output_gpu, 1, l.state_gpu, 1); - axpy_ongpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); + copy_gpu(l.hidden * l.batch, input_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); - s.input = l.state_gpu; - s.delta = self_layer.delta_gpu; + s.input_gpu = l.state_gpu; + s.delta_gpu = self_layer.delta_gpu; backward_convolutional_layer_gpu(output_layer, s); l.state_gpu -= l.hidden*l.batch; - s.input = l.state_gpu; - s.delta = self_layer.delta_gpu - l.hidden*l.batch; - if (i == 0) s.delta = 0; + s.input_gpu = l.state_gpu; + s.delta_gpu = self_layer.delta_gpu - l.hidden*l.batch; + if (i == 0) s.delta_gpu = 0; backward_convolutional_layer_gpu(self_layer, s); - copy_ongpu(l.hidden*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); - if (i > 0 && l.shortcut) axpy_ongpu(l.hidden*l.batch, 1, self_layer.delta_gpu, 1, self_layer.delta_gpu - l.hidden*l.batch, 1); - s.input = state.input + i*l.inputs*l.batch; - if(state.delta) s.delta = state.delta + i*l.inputs*l.batch; - else s.delta = 0; + copy_gpu(l.hidden*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); + if (i > 0 && l.shortcut) axpy_gpu(l.hidden*l.batch, 1, self_layer.delta_gpu, 1, self_layer.delta_gpu - l.hidden*l.batch, 1); + s.input_gpu = net.input_gpu + i*l.inputs*l.batch; + if(net.delta_gpu) s.delta_gpu = net.delta_gpu + i*l.inputs*l.batch; + else s.delta_gpu = 0; backward_convolutional_layer_gpu(input_layer, s); increment_layer(&input_layer, -1); diff --git a/image.darknet/src/crnn_layer.h b/image.darknet/src/crnn_layer.h index 0da942e..515f378 100644 --- a/image.darknet/src/crnn_layer.h +++ b/image.darknet/src/crnn_layer.h @@ -8,14 +8,14 @@ layer make_crnn_layer(int batch, int h, int w, int c, int hidden_filters, int output_filters, int steps, ACTIVATION activation, int batch_normalize); -void forward_crnn_layer(layer l, network_state state); -void backward_crnn_layer(layer l, network_state state); -void update_crnn_layer(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_crnn_layer(layer l, network net); +void backward_crnn_layer(layer l, network net); +void update_crnn_layer(layer l, update_args a); #ifdef GPU -void forward_crnn_layer_gpu(layer l, network_state state); -void backward_crnn_layer_gpu(layer l, network_state state); -void update_crnn_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_crnn_layer_gpu(layer l, network net); +void backward_crnn_layer_gpu(layer l, network net); +void update_crnn_layer_gpu(layer l, update_args a); void push_crnn_layer(layer l); void pull_crnn_layer(layer l); #endif diff --git a/image.darknet/src/crop_layer.c b/image.darknet/src/crop_layer.c index 11c59b4..3b91852 100644 --- a/image.darknet/src/crop_layer.c +++ b/image.darknet/src/crop_layer.c @@ -10,8 +10,8 @@ image get_crop_image(crop_layer l) return float_to_image(w,h,c,l.output); } -void backward_crop_layer(const crop_layer l, network_state state){} -void backward_crop_layer_gpu(const crop_layer l, network_state state){} +void backward_crop_layer(const crop_layer l, network net){} +void backward_crop_layer_gpu(const crop_layer l, network net){} crop_layer make_crop_layer(int batch, int h, int w, int c, int crop_height, int crop_width, int flip, float angle, float saturation, float exposure) { @@ -64,7 +64,7 @@ void resize_crop_layer(layer *l, int w, int h) } -void forward_crop_layer(const crop_layer l, network_state state) +void forward_crop_layer(const crop_layer l, network net) { int i,j,c,b,row,col; int index; @@ -78,7 +78,7 @@ void forward_crop_layer(const crop_layer l, network_state state) scale = 1; trans = 0; } - if(!state.train){ + if(!net.train){ flip = 0; dh = (l.h - l.out_h)/2; dw = (l.w - l.out_w)/2; @@ -94,7 +94,7 @@ void forward_crop_layer(const crop_layer l, network_state state) } row = i + dh; index = col+l.w*(row+l.h*(c + l.c*b)); - l.output[count++] = state.input[index]*scale + trans; + l.output[count++] = net.input[index]*scale + trans; } } } diff --git a/image.darknet/src/crop_layer.h b/image.darknet/src/crop_layer.h index 3aa2d3d..3b5883c 100644 --- a/image.darknet/src/crop_layer.h +++ b/image.darknet/src/crop_layer.h @@ -9,11 +9,11 @@ typedef layer crop_layer; image get_crop_image(crop_layer l); crop_layer make_crop_layer(int batch, int h, int w, int c, int crop_height, int crop_width, int flip, float angle, float saturation, float exposure); -void forward_crop_layer(const crop_layer l, network_state state); +void forward_crop_layer(const crop_layer l, network net); void resize_crop_layer(layer *l, int w, int h); #ifdef GPU -void forward_crop_layer_gpu(crop_layer l, network_state state); +void forward_crop_layer_gpu(crop_layer l, network net); #endif #endif diff --git a/image.darknet/src/crop_layer_kernels.cu b/image.darknet/src/crop_layer_kernels.cu index 8a08630..b5b9f55 100644 --- a/image.darknet/src/crop_layer_kernels.cu +++ b/image.darknet/src/crop_layer_kernels.cu @@ -113,9 +113,9 @@ __global__ void levels_image_kernel(float *image, float *rand, int batch, int w, float r3 = rand[8*id + 3]; saturation = r0*(saturation - 1) + 1; - saturation = (r1 > .5) ? 1./saturation : saturation; + saturation = (r1 > .5f) ? 1.f/saturation : saturation; exposure = r2*(exposure - 1) + 1; - exposure = (r3 > .5) ? 1./exposure : exposure; + exposure = (r3 > .5f) ? 1.f/exposure : exposure; size_t offset = id * h * w * 3; image += offset; @@ -131,9 +131,9 @@ __global__ void levels_image_kernel(float *image, float *rand, int batch, int w, } else { shift = 0; } - image[x + w*(y + h*0)] = rgb.x*scale + translate + (rshift - .5)*shift; - image[x + w*(y + h*1)] = rgb.y*scale + translate + (gshift - .5)*shift; - image[x + w*(y + h*2)] = rgb.z*scale + translate + (bshift - .5)*shift; + image[x + w*(y + h*0)] = rgb.x*scale + translate + (rshift - .5f)*shift; + image[x + w*(y + h*1)] = rgb.y*scale + translate + (gshift - .5f)*shift; + image[x + w*(y + h*2)] = rgb.z*scale + translate + (bshift - .5f)*shift; } __global__ void forward_crop_layer_kernel(float *input, float *rand, int size, int c, int h, int w, int crop_height, int crop_width, int train, int flip, float angle, float *output) @@ -141,8 +141,8 @@ __global__ void forward_crop_layer_kernel(float *input, float *rand, int size, i int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if(id >= size) return; - float cx = w/2.; - float cy = h/2.; + float cx = w/2.f; + float cy = h/2.f; int count = id; int j = id % crop_width; @@ -160,11 +160,11 @@ __global__ void forward_crop_layer_kernel(float *input, float *rand, int size, i float dw = (w - crop_width)*r4; float dh = (h - crop_height)*r5; - flip = (flip && (r6 > .5)); + flip = (flip && (r6 > .5f)); angle = 2*angle*r7 - angle; if(!train){ - dw = (w - crop_width)/2.; - dh = (h - crop_height)/2.; + dw = (w - crop_width)/2.f; + dh = (h - crop_height)/2.f; flip = 0; angle = 0; } @@ -174,17 +174,17 @@ __global__ void forward_crop_layer_kernel(float *input, float *rand, int size, i float x = (flip) ? w - dw - j - 1 : j + dw; float y = i + dh; - float rx = cos(angle)*(x-cx) - sin(angle)*(y-cy) + cx; - float ry = sin(angle)*(x-cx) + cos(angle)*(y-cy) + cy; + float rx = cosf(angle)*(x-cx) - sinf(angle)*(y-cy) + cx; + float ry = sinf(angle)*(x-cx) + cosf(angle)*(y-cy) + cy; output[count] = bilinear_interpolate_kernel(input, w, h, rx, ry, k); } -extern "C" void forward_crop_layer_gpu(crop_layer layer, network_state state) +extern "C" void forward_crop_layer_gpu(crop_layer layer, network net) { cuda_random(layer.rand_gpu, layer.batch*8); - float radians = layer.angle*3.14159265/180.; + float radians = layer.angle*3.14159265f/180.f; float scale = 2; float translate = -1; @@ -195,12 +195,12 @@ extern "C" void forward_crop_layer_gpu(crop_layer layer, network_state state) int size = layer.batch * layer.w * layer.h; - levels_image_kernel<<>>(state.input, layer.rand_gpu, layer.batch, layer.w, layer.h, state.train, layer.saturation, layer.exposure, translate, scale, layer.shift); + levels_image_kernel<<>>(net.input_gpu, layer.rand_gpu, layer.batch, layer.w, layer.h, net.train, layer.saturation, layer.exposure, translate, scale, layer.shift); check_error(cudaPeekAtLastError()); size = layer.batch*layer.c*layer.out_w*layer.out_h; - forward_crop_layer_kernel<<>>(state.input, layer.rand_gpu, size, layer.c, layer.h, layer.w, layer.out_h, layer.out_w, state.train, layer.flip, radians, layer.output_gpu); + forward_crop_layer_kernel<<>>(net.input_gpu, layer.rand_gpu, size, layer.c, layer.h, layer.w, layer.out_h, layer.out_w, net.train, layer.flip, radians, layer.output_gpu); check_error(cudaPeekAtLastError()); /* diff --git a/image.darknet/src/cuda.c b/image.darknet/src/cuda.c index 1b51271..48aba6e 100644 --- a/image.darknet/src/cuda.c +++ b/image.darknet/src/cuda.c @@ -5,7 +5,7 @@ int gpu_index = 0; #include "cuda.h" #include "utils.h" #include "blas.h" -#include "assert.h" +#include #include #include @@ -96,6 +96,8 @@ float *cuda_make_array(float *x, size_t n) if(x){ status = cudaMemcpy(x_gpu, x, size, cudaMemcpyHostToDevice); check_error(status); + } else { + fill_gpu(n, 0, x_gpu, 1); } if(!x_gpu) error("Cuda malloc failed\n"); return x_gpu; @@ -128,12 +130,17 @@ float cuda_compare(float *x_gpu, float *x, size_t n, char *s) return err; } -int *cuda_make_int_array(size_t n) +int *cuda_make_int_array(int *x, size_t n) { int *x_gpu; size_t size = sizeof(int)*n; cudaError_t status = cudaMalloc((void **)&x_gpu, size); check_error(status); + if(x){ + status = cudaMemcpy(x_gpu, x, size, cudaMemcpyHostToDevice); + check_error(status); + } + if(!x_gpu) error("Cuda malloc failed\n"); return x_gpu; } @@ -157,4 +164,15 @@ void cuda_pull_array(float *x_gpu, float *x, size_t n) check_error(status); } +float cuda_mag_array(float *x_gpu, size_t n) +{ + float *temp = calloc(n, sizeof(float)); + cuda_pull_array(x_gpu, temp, n); + float m = mag_array(temp, n); + free(temp); + return m; +} +#else +void cuda_set_device(int n){} + #endif diff --git a/image.darknet/src/cuda.h b/image.darknet/src/cuda.h index 29b1eef..a1bc216 100644 --- a/image.darknet/src/cuda.h +++ b/image.darknet/src/cuda.h @@ -1,28 +1,13 @@ #ifndef CUDA_H #define CUDA_H -extern int gpu_index; +#include "darknet.h" #ifdef GPU -#define BLOCK 512 - -#include "cuda_runtime.h" -#include "curand.h" -#include "cublas_v2.h" - -#ifdef CUDNN -#include "cudnn.h" -#endif - void check_error(cudaError_t status); cublasHandle_t blas_handle(); -float *cuda_make_array(float *x, size_t n); -int *cuda_make_int_array(size_t n); -void cuda_push_array(float *x_gpu, float *x, size_t n); -void cuda_pull_array(float *x_gpu, float *x, size_t n); -void cuda_set_device(int n); -void cuda_free(float *x_gpu); +int *cuda_make_int_array(int *x, size_t n); void cuda_random(float *x_gpu, size_t n); float cuda_compare(float *x_gpu, float *x, size_t n, char *s); dim3 cuda_gridsize(size_t n); diff --git a/image.darknet/src/darknet.c b/image.darknet/src/darknet.c deleted file mode 100644 index 6e56072..0000000 --- a/image.darknet/src/darknet.c +++ /dev/null @@ -1,452 +0,0 @@ -#include -#include -#include - -#include "parser.h" -#include "utils.h" -#include "cuda.h" -#include "blas.h" -#include "connected_layer.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -extern void predict_classifier(char *datacfg, char *cfgfile, char *weightfile, char *filename, int top); -extern void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh); -extern void run_voxel(int argc, char **argv); -extern void run_yolo(int argc, char **argv); -extern void run_detector(int argc, char **argv); -extern void run_coco(int argc, char **argv); -extern void run_writing(int argc, char **argv); -extern void run_captcha(int argc, char **argv); -extern void run_nightmare(int argc, char **argv); -extern void run_dice(int argc, char **argv); -extern void run_compare(int argc, char **argv); -extern void run_classifier(int argc, char **argv); -extern void run_char_rnn(int argc, char **argv); -extern void run_vid_rnn(int argc, char **argv); -extern void run_tag(int argc, char **argv); -extern void run_cifar(int argc, char **argv); -extern void run_go(int argc, char **argv); -extern void run_art(int argc, char **argv); -extern void run_super(int argc, char **argv); - -void average(int argc, char *argv[]) -{ - char *cfgfile = argv[2]; - char *outfile = argv[3]; - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - network sum = parse_network_cfg(cfgfile); - - char *weightfile = argv[4]; - load_weights(&sum, weightfile); - - int i, j; - int n = argc - 5; - for(i = 0; i < n; ++i){ - weightfile = argv[i+5]; - load_weights(&net, weightfile); - for(j = 0; j < net.n; ++j){ - layer l = net.layers[j]; - layer out = sum.layers[j]; - if(l.type == CONVOLUTIONAL){ - int num = l.n*l.c*l.size*l.size; - axpy_cpu(l.n, 1, l.biases, 1, out.biases, 1); - axpy_cpu(num, 1, l.weights, 1, out.weights, 1); - if(l.batch_normalize){ - axpy_cpu(l.n, 1, l.scales, 1, out.scales, 1); - axpy_cpu(l.n, 1, l.rolling_mean, 1, out.rolling_mean, 1); - axpy_cpu(l.n, 1, l.rolling_variance, 1, out.rolling_variance, 1); - } - } - if(l.type == CONNECTED){ - axpy_cpu(l.outputs, 1, l.biases, 1, out.biases, 1); - axpy_cpu(l.outputs*l.inputs, 1, l.weights, 1, out.weights, 1); - } - } - } - n = n+1; - for(j = 0; j < net.n; ++j){ - layer l = sum.layers[j]; - if(l.type == CONVOLUTIONAL){ - int num = l.n*l.c*l.size*l.size; - scal_cpu(l.n, 1./n, l.biases, 1); - scal_cpu(num, 1./n, l.weights, 1); - if(l.batch_normalize){ - scal_cpu(l.n, 1./n, l.scales, 1); - scal_cpu(l.n, 1./n, l.rolling_mean, 1); - scal_cpu(l.n, 1./n, l.rolling_variance, 1); - } - } - if(l.type == CONNECTED){ - scal_cpu(l.outputs, 1./n, l.biases, 1); - scal_cpu(l.outputs*l.inputs, 1./n, l.weights, 1); - } - } - save_weights(sum, outfile); -} - -void speed(char *cfgfile, int tics) -{ - if (tics == 0) tics = 1000; - network net = parse_network_cfg(cfgfile); - set_batch_network(&net, 1); - int i; - time_t start = time(0); - image im = make_image(net.w, net.h, net.c); - for(i = 0; i < tics; ++i){ - network_predict(net, im.data); - } - double t = difftime(time(0), start); - printf("\n%d evals, %f Seconds\n", tics, t); - printf("Speed: %f sec/eval\n", t/tics); - printf("Speed: %f Hz\n", tics/t); -} - -void operations(char *cfgfile) -{ - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - int i; - long ops = 0; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; - if(l.type == CONVOLUTIONAL){ - ops += 2l * l.n * l.size*l.size*l.c * l.out_h*l.out_w; - } else if(l.type == CONNECTED){ - ops += 2l * l.inputs * l.outputs; - } - } - printf("Floating Point Operations: %ld\n", ops); - printf("Floating Point Operations: %.2f Bn\n", (float)ops/1000000000.); -} - -void oneoff(char *cfgfile, char *weightfile, char *outfile) -{ - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - int oldn = net.layers[net.n - 2].n; - int c = net.layers[net.n - 2].c; - scal_cpu(oldn*c, .1, net.layers[net.n - 2].weights, 1); - scal_cpu(oldn, 0, net.layers[net.n - 2].biases, 1); - net.layers[net.n - 2].n = 9418; - net.layers[net.n - 2].biases += 5; - net.layers[net.n - 2].weights += 5*c; - if(weightfile){ - load_weights(&net, weightfile); - } - net.layers[net.n - 2].biases -= 5; - net.layers[net.n - 2].weights -= 5*c; - net.layers[net.n - 2].n = oldn; - printf("%d\n", oldn); - layer l = net.layers[net.n - 2]; - copy_cpu(l.n/3, l.biases, 1, l.biases + l.n/3, 1); - copy_cpu(l.n/3, l.biases, 1, l.biases + 2*l.n/3, 1); - copy_cpu(l.n/3*l.c, l.weights, 1, l.weights + l.n/3*l.c, 1); - copy_cpu(l.n/3*l.c, l.weights, 1, l.weights + 2*l.n/3*l.c, 1); - *net.seen = 0; - save_weights(net, outfile); -} - -void partial(char *cfgfile, char *weightfile, char *outfile, int max) -{ - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights_upto(&net, weightfile, max); - } - *net.seen = 0; - save_weights_upto(net, outfile, max); -} - -#include "convolutional_layer.h" -void rescale_net(char *cfgfile, char *weightfile, char *outfile) -{ - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int i; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; - if(l.type == CONVOLUTIONAL){ - rescale_weights(l, 2, -.5); - break; - } - } - save_weights(net, outfile); -} - -void rgbgr_net(char *cfgfile, char *weightfile, char *outfile) -{ - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int i; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; - if(l.type == CONVOLUTIONAL){ - rgbgr_weights(l); - break; - } - } - save_weights(net, outfile); -} - -void reset_normalize_net(char *cfgfile, char *weightfile, char *outfile) -{ - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if (weightfile) { - load_weights(&net, weightfile); - } - int i; - for (i = 0; i < net.n; ++i) { - layer l = net.layers[i]; - if (l.type == CONVOLUTIONAL && l.batch_normalize) { - denormalize_convolutional_layer(l); - } - if (l.type == CONNECTED && l.batch_normalize) { - denormalize_connected_layer(l); - } - if (l.type == GRU && l.batch_normalize) { - denormalize_connected_layer(*l.input_z_layer); - denormalize_connected_layer(*l.input_r_layer); - denormalize_connected_layer(*l.input_h_layer); - denormalize_connected_layer(*l.state_z_layer); - denormalize_connected_layer(*l.state_r_layer); - denormalize_connected_layer(*l.state_h_layer); - } - } - save_weights(net, outfile); -} - -layer normalize_layer(layer l, int n) -{ - int j; - l.batch_normalize=1; - l.scales = calloc(n, sizeof(float)); - for(j = 0; j < n; ++j){ - l.scales[j] = 1; - } - l.rolling_mean = calloc(n, sizeof(float)); - l.rolling_variance = calloc(n, sizeof(float)); - return l; -} - -void normalize_net(char *cfgfile, char *weightfile, char *outfile) -{ - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int i; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; - if(l.type == CONVOLUTIONAL && !l.batch_normalize){ - net.layers[i] = normalize_layer(l, l.n); - } - if (l.type == CONNECTED && !l.batch_normalize) { - net.layers[i] = normalize_layer(l, l.outputs); - } - if (l.type == GRU && l.batch_normalize) { - *l.input_z_layer = normalize_layer(*l.input_z_layer, l.input_z_layer->outputs); - *l.input_r_layer = normalize_layer(*l.input_r_layer, l.input_r_layer->outputs); - *l.input_h_layer = normalize_layer(*l.input_h_layer, l.input_h_layer->outputs); - *l.state_z_layer = normalize_layer(*l.state_z_layer, l.state_z_layer->outputs); - *l.state_r_layer = normalize_layer(*l.state_r_layer, l.state_r_layer->outputs); - *l.state_h_layer = normalize_layer(*l.state_h_layer, l.state_h_layer->outputs); - net.layers[i].batch_normalize=1; - } - } - save_weights(net, outfile); -} - -void statistics_net(char *cfgfile, char *weightfile) -{ - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if (weightfile) { - load_weights(&net, weightfile); - } - int i; - for (i = 0; i < net.n; ++i) { - layer l = net.layers[i]; - if (l.type == CONNECTED && l.batch_normalize) { - printf("Connected Layer %d\n", i); - statistics_connected_layer(l); - } - if (l.type == GRU && l.batch_normalize) { - printf("GRU Layer %d\n", i); - printf("Input Z\n"); - statistics_connected_layer(*l.input_z_layer); - printf("Input R\n"); - statistics_connected_layer(*l.input_r_layer); - printf("Input H\n"); - statistics_connected_layer(*l.input_h_layer); - printf("State Z\n"); - statistics_connected_layer(*l.state_z_layer); - printf("State R\n"); - statistics_connected_layer(*l.state_r_layer); - printf("State H\n"); - statistics_connected_layer(*l.state_h_layer); - } - printf("\n"); - } -} - -void denormalize_net(char *cfgfile, char *weightfile, char *outfile) -{ - gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if (weightfile) { - load_weights(&net, weightfile); - } - int i; - for (i = 0; i < net.n; ++i) { - layer l = net.layers[i]; - if (l.type == CONVOLUTIONAL && l.batch_normalize) { - denormalize_convolutional_layer(l); - net.layers[i].batch_normalize=0; - } - if (l.type == CONNECTED && l.batch_normalize) { - denormalize_connected_layer(l); - net.layers[i].batch_normalize=0; - } - if (l.type == GRU && l.batch_normalize) { - denormalize_connected_layer(*l.input_z_layer); - denormalize_connected_layer(*l.input_r_layer); - denormalize_connected_layer(*l.input_h_layer); - denormalize_connected_layer(*l.state_z_layer); - denormalize_connected_layer(*l.state_r_layer); - denormalize_connected_layer(*l.state_h_layer); - l.input_z_layer->batch_normalize = 0; - l.input_r_layer->batch_normalize = 0; - l.input_h_layer->batch_normalize = 0; - l.state_z_layer->batch_normalize = 0; - l.state_r_layer->batch_normalize = 0; - l.state_h_layer->batch_normalize = 0; - net.layers[i].batch_normalize=0; - } - } - save_weights(net, outfile); -} - -void visualize(char *cfgfile, char *weightfile) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - visualize_network(net); -#ifdef OPENCV - cvWaitKey(0); -#endif -} - -int main(int argc, char **argv) -{ - //test_resize("data/bad.jpg"); - //test_box(); - //test_convolutional_layer(); - if(argc < 2){ - fprintf(stderr, "usage: %s \n", argv[0]); - return 0; - } - gpu_index = find_int_arg(argc, argv, "-i", 0); - if(find_arg(argc, argv, "-nogpu")) { - gpu_index = -1; - } - -#ifndef GPU - gpu_index = -1; -#else - if(gpu_index >= 0){ - cuda_set_device(gpu_index); - } -#endif - - if (0 == strcmp(argv[1], "average")){ - average(argc, argv); - } else if (0 == strcmp(argv[1], "yolo")){ - run_yolo(argc, argv); - } else if (0 == strcmp(argv[1], "voxel")){ - run_voxel(argc, argv); - } else if (0 == strcmp(argv[1], "super")){ - run_super(argc, argv); - } else if (0 == strcmp(argv[1], "detector")){ - run_detector(argc, argv); - } else if (0 == strcmp(argv[1], "detect")){ - float thresh = find_float_arg(argc, argv, "-thresh", .24); - char *filename = (argc > 4) ? argv[4]: 0; - test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5); - } else if (0 == strcmp(argv[1], "cifar")){ - run_cifar(argc, argv); - } else if (0 == strcmp(argv[1], "go")){ - run_go(argc, argv); - } else if (0 == strcmp(argv[1], "rnn")){ - run_char_rnn(argc, argv); - } else if (0 == strcmp(argv[1], "vid")){ - run_vid_rnn(argc, argv); - } else if (0 == strcmp(argv[1], "coco")){ - run_coco(argc, argv); - } else if (0 == strcmp(argv[1], "classify")){ - predict_classifier("cfg/imagenet1k.data", argv[2], argv[3], argv[4], 5); - } else if (0 == strcmp(argv[1], "classifier")){ - run_classifier(argc, argv); - } else if (0 == strcmp(argv[1], "art")){ - run_art(argc, argv); - } else if (0 == strcmp(argv[1], "tag")){ - run_tag(argc, argv); - } else if (0 == strcmp(argv[1], "compare")){ - run_compare(argc, argv); - } else if (0 == strcmp(argv[1], "dice")){ - run_dice(argc, argv); - } else if (0 == strcmp(argv[1], "writing")){ - run_writing(argc, argv); - } else if (0 == strcmp(argv[1], "3d")){ - composite_3d(argv[2], argv[3], argv[4], (argc > 5) ? atof(argv[5]) : 0); - } else if (0 == strcmp(argv[1], "test")){ - test_resize(argv[2]); - } else if (0 == strcmp(argv[1], "captcha")){ - run_captcha(argc, argv); - } else if (0 == strcmp(argv[1], "nightmare")){ - run_nightmare(argc, argv); - } else if (0 == strcmp(argv[1], "rgbgr")){ - rgbgr_net(argv[2], argv[3], argv[4]); - } else if (0 == strcmp(argv[1], "reset")){ - reset_normalize_net(argv[2], argv[3], argv[4]); - } else if (0 == strcmp(argv[1], "denormalize")){ - denormalize_net(argv[2], argv[3], argv[4]); - } else if (0 == strcmp(argv[1], "statistics")){ - statistics_net(argv[2], argv[3]); - } else if (0 == strcmp(argv[1], "normalize")){ - normalize_net(argv[2], argv[3], argv[4]); - } else if (0 == strcmp(argv[1], "rescale")){ - rescale_net(argv[2], argv[3], argv[4]); - } else if (0 == strcmp(argv[1], "ops")){ - operations(argv[2]); - } else if (0 == strcmp(argv[1], "speed")){ - speed(argv[2], (argc > 3 && argv[3]) ? atoi(argv[3]) : 0); - } else if (0 == strcmp(argv[1], "oneoff")){ - oneoff(argv[2], argv[3], argv[4]); - } else if (0 == strcmp(argv[1], "partial")){ - partial(argv[2], argv[3], argv[4], atoi(argv[5])); - } else if (0 == strcmp(argv[1], "average")){ - average(argc, argv); - } else if (0 == strcmp(argv[1], "visualize")){ - visualize(argv[2], (argc > 3) ? argv[3] : 0); - } else if (0 == strcmp(argv[1], "imtest")){ - test_resize(argv[2]); - } else { - fprintf(stderr, "Not an option: %s\n", argv[1]); - } - return 0; -} - diff --git a/image.darknet/src/data.c b/image.darknet/src/data.c index 05e5a91..59051b4 100644 --- a/image.darknet/src/data.c +++ b/image.darknet/src/data.c @@ -102,7 +102,7 @@ matrix load_image_paths(char **paths, int n, int w, int h) return X; } -matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure) +matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure, int center) { int i; matrix X; @@ -112,7 +112,12 @@ matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, for(i = 0; i < n; ++i){ image im = load_image_color(paths[i], 0, 0); - image crop = random_augment_image(im, angle, aspect, min, max, size); + image crop; + if(center){ + crop = center_crop_image(im, size, size); + } else { + crop = random_augment_image(im, angle, aspect, min, max, size, size); + } int flip = rand()%2; if (flip) flip_image(crop); random_distort_image(crop, hue, saturation, exposure); @@ -122,6 +127,7 @@ matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, show_image(crop, "crop"); cvWaitKey(0); */ + //grayscale_image_3c(crop); free_image(im); X.vals[i] = crop.data; X.cols = crop.h*crop.w*crop.c; @@ -132,14 +138,18 @@ matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, box_label *read_boxes(char *filename, int *n) { - box_label *boxes = calloc(1, sizeof(box_label)); FILE *file = fopen(filename, "r"); if(!file) file_error(filename); float x, y, h, w; int id; int count = 0; + int size = 64; + box_label *boxes = calloc(size, sizeof(box_label)); while(fscanf(file, "%d %f %f %f %f", &id, &x, &y, &w, &h) == 5){ - boxes = realloc(boxes, (count+1)*sizeof(box_label)); + if(count == size) { + size = size * 2; + boxes = realloc(boxes, size*sizeof(box_label)); + } boxes[count].id = id; boxes[count].x = x; boxes[count].y = y; @@ -221,7 +231,7 @@ void fill_truth_swag(char *path, float *truth, int classes, int flip, float dx, int id; int i; - for (i = 0; i < count && i < 30; ++i) { + for (i = 0; i < count && i < 90; ++i) { x = boxes[i].x; y = boxes[i].y; w = boxes[i].w; @@ -290,6 +300,150 @@ void fill_truth_region(char *path, float *truth, int classes, int num_boxes, int free(boxes); } +void load_rle(image im, int *rle, int n) +{ + int count = 0; + int curr = 0; + int i,j; + for(i = 0; i < n; ++i){ + for(j = 0; j < rle[i]; ++j){ + im.data[count++] = curr; + } + curr = 1 - curr; + } + for(; count < im.h*im.w*im.c; ++count){ + im.data[count] = curr; + } +} + +void or_image(image src, image dest, int c) +{ + int i; + for(i = 0; i < src.w*src.h; ++i){ + if(src.data[i]) dest.data[dest.w*dest.h*c + i] = 1; + } +} + +void exclusive_image(image src) +{ + int k, j, i; + int s = src.w*src.h; + for(k = 0; k < src.c-1; ++k){ + for(i = 0; i < s; ++i){ + if (src.data[k*s + i]){ + for(j = k+1; j < src.c; ++j){ + src.data[j*s + i] = 0; + } + } + } + } +} + +box bound_image(image im) +{ + int x,y; + int minx = im.w; + int miny = im.h; + int maxx = 0; + int maxy = 0; + for(y = 0; y < im.h; ++y){ + for(x = 0; x < im.w; ++x){ + if(im.data[y*im.w + x]){ + minx = (x < minx) ? x : minx; + miny = (y < miny) ? y : miny; + maxx = (x > maxx) ? x : maxx; + maxy = (y > maxy) ? y : maxy; + } + } + } + box b = {minx, miny, maxx-minx + 1, maxy-miny + 1}; + //printf("%f %f %f %f\n", b.x, b.y, b.w, b.h); + return b; +} + +void fill_truth_iseg(char *path, int num_boxes, float *truth, int classes, int w, int h, augment_args aug, int flip, int mw, int mh) +{ + char labelpath[4096]; + find_replace(path, "images", "mask", labelpath); + find_replace(labelpath, "JPEGImages", "mask", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + FILE *file = fopen(labelpath, "r"); + if(!file) file_error(labelpath); + char buff[32788]; + int id; + int i = 0; + int j; + image part = make_image(w, h, 1); + while((fscanf(file, "%d %s", &id, buff) == 2) && i < num_boxes){ + int n = 0; + int *rle = read_intlist(buff, &n, 0); + load_rle(part, rle, n); + image sized = rotate_crop_image(part, aug.rad, aug.scale, aug.w, aug.h, aug.dx, aug.dy, aug.aspect); + if(flip) flip_image(sized); + + image mask = resize_image(sized, mw, mh); + truth[i*(mw*mh+1)] = id; + for(j = 0; j < mw*mh; ++j){ + truth[i*(mw*mh + 1) + 1 + j] = mask.data[j]; + } + ++i; + + free_image(mask); + free_image(sized); + free(rle); + } + if(i < num_boxes) truth[i*(mw*mh+1)] = -1; + fclose(file); + free_image(part); +} + +void fill_truth_mask(char *path, int num_boxes, float *truth, int classes, int w, int h, augment_args aug, int flip, int mw, int mh) +{ + char labelpath[4096]; + find_replace(path, "images", "mask", labelpath); + find_replace(labelpath, "JPEGImages", "mask", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + FILE *file = fopen(labelpath, "r"); + if(!file) file_error(labelpath); + char buff[32788]; + int id; + int i = 0; + image part = make_image(w, h, 1); + while((fscanf(file, "%d %s", &id, buff) == 2) && i < num_boxes){ + int n = 0; + int *rle = read_intlist(buff, &n, 0); + load_rle(part, rle, n); + image sized = rotate_crop_image(part, aug.rad, aug.scale, aug.w, aug.h, aug.dx, aug.dy, aug.aspect); + if(flip) flip_image(sized); + box b = bound_image(sized); + if(b.w > 0){ + image crop = crop_image(sized, b.x, b.y, b.w, b.h); + image mask = resize_image(crop, mw, mh); + truth[i*(4 + mw*mh + 1) + 0] = (b.x + b.w/2.)/sized.w; + truth[i*(4 + mw*mh + 1) + 1] = (b.y + b.h/2.)/sized.h; + truth[i*(4 + mw*mh + 1) + 2] = b.w/sized.w; + truth[i*(4 + mw*mh + 1) + 3] = b.h/sized.h; + int j; + for(j = 0; j < mw*mh; ++j){ + truth[i*(4 + mw*mh + 1) + 4 + j] = mask.data[j]; + } + truth[i*(4 + mw*mh + 1) + 4 + mw*mh] = id; + free_image(crop); + free_image(mask); + ++i; + } + free_image(sized); + free(rle); + } + fclose(file); + free_image(part); +} + + void fill_truth_detection(char *path, int num_boxes, float *truth, int classes, int flip, float dx, float dy, float sx, float sy) { char labelpath[4096]; @@ -309,6 +463,7 @@ void fill_truth_detection(char *path, int num_boxes, float *truth, int classes, float x,y,w,h; int id; int i; + int sub = 0; for (i = 0; i < count; ++i) { x = boxes[i].x; @@ -317,13 +472,16 @@ void fill_truth_detection(char *path, int num_boxes, float *truth, int classes, h = boxes[i].h; id = boxes[i].id; - if ((w < .005 || h < .005)) continue; + if ((w < .001 || h < .001)) { + ++sub; + continue; + } - truth[i*5+0] = x; - truth[i*5+1] = y; - truth[i*5+2] = w; - truth[i*5+3] = h; - truth[i*5+4] = id; + truth[(i-sub)*5+0] = x; + truth[(i-sub)*5+1] = y; + truth[(i-sub)*5+2] = w; + truth[(i-sub)*5+3] = h; + truth[(i-sub)*5+4] = id; } free(boxes); } @@ -391,9 +549,10 @@ void fill_truth(char *path, char **labels, int k, float *truth) if(strstr(path, labels[i])){ truth[i] = 1; ++count; + //printf("%s %s %d\n", path, labels[i], i); } } - if(count != 1) printf("Too many or too few labels: %d, %s\n", count, path); + if(count != 1 && (k != 1 || count != 0)) printf("Too many or too few labels: %d, %s\n", count, path); } void fill_hierarchy(float *truth, int k, tree *hierarchy) @@ -428,6 +587,36 @@ void fill_hierarchy(float *truth, int k, tree *hierarchy) } } +matrix load_regression_labels_paths(char **paths, int n, int k) +{ + matrix y = make_matrix(n, k); + int i,j; + for(i = 0; i < n; ++i){ + char labelpath[4096]; + find_replace(paths[i], "images", "labels", labelpath); + find_replace(labelpath, "JPEGImages", "labels", labelpath); + find_replace(labelpath, ".BMP", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPeG", ".txt", labelpath); + find_replace(labelpath, ".Jpeg", ".txt", labelpath); + find_replace(labelpath, ".PNG", ".txt", labelpath); + find_replace(labelpath, ".TIF", ".txt", labelpath); + find_replace(labelpath, ".bmp", ".txt", labelpath); + find_replace(labelpath, ".jpeg", ".txt", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".png", ".txt", labelpath); + find_replace(labelpath, ".tif", ".txt", labelpath); + + FILE *file = fopen(labelpath, "r"); + for(j = 0; j < k; ++j){ + fscanf(file, "%f", &(y.vals[i][j])); + } + fclose(file); + } + return y; +} + matrix load_labels_paths(char **paths, int n, char **labels, int k, tree *hierarchy) { matrix y = make_matrix(n, k); @@ -445,18 +634,14 @@ matrix load_tags_paths(char **paths, int n, int k) { matrix y = make_matrix(n, k); int i; - int count = 0; + //int count = 0; for(i = 0; i < n; ++i){ char label[4096]; - find_replace(paths[i], "imgs", "labels", label); - find_replace(label, "_iconl.jpeg", ".txt", label); + find_replace(paths[i], "images", "labels", label); + find_replace(label, ".jpg", ".txt", label); FILE *file = fopen(label, "r"); - if(!file){ - find_replace(label, "labels", "labels2", label); - file = fopen(label, "r"); - if(!file) continue; - } - ++count; + if (!file) continue; + //++count; int tag; while(fscanf(file, "%d", &tag) == 1){ if(tag < k){ @@ -465,7 +650,7 @@ matrix load_tags_paths(char **paths, int n, int k) } fclose(file); } - printf("%d/%d\n", count, n); + //printf("%d/%d\n", count, n); return y; } @@ -488,6 +673,195 @@ void free_data(data d) } } +image get_segmentation_image(char *path, int w, int h, int classes) +{ + char labelpath[4096]; + find_replace(path, "images", "mask", labelpath); + find_replace(labelpath, "JPEGImages", "mask", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + image mask = make_image(w, h, classes); + FILE *file = fopen(labelpath, "r"); + if(!file) file_error(labelpath); + char buff[32788]; + int id; + image part = make_image(w, h, 1); + while(fscanf(file, "%d %s", &id, buff) == 2){ + int n = 0; + int *rle = read_intlist(buff, &n, 0); + load_rle(part, rle, n); + or_image(part, mask, id); + free(rle); + } + //exclusive_image(mask); + fclose(file); + free_image(part); + return mask; +} + +image get_segmentation_image2(char *path, int w, int h, int classes) +{ + char labelpath[4096]; + find_replace(path, "images", "mask", labelpath); + find_replace(labelpath, "JPEGImages", "mask", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + image mask = make_image(w, h, classes+1); + int i; + for(i = 0; i < w*h; ++i){ + mask.data[w*h*classes + i] = 1; + } + FILE *file = fopen(labelpath, "r"); + if(!file) file_error(labelpath); + char buff[32788]; + int id; + image part = make_image(w, h, 1); + while(fscanf(file, "%d %s", &id, buff) == 2){ + int n = 0; + int *rle = read_intlist(buff, &n, 0); + load_rle(part, rle, n); + or_image(part, mask, id); + for(i = 0; i < w*h; ++i){ + if(part.data[i]) mask.data[w*h*classes + i] = 0; + } + free(rle); + } + //exclusive_image(mask); + fclose(file); + free_image(part); + return mask; +} + +data load_data_seg(int n, char **paths, int m, int w, int h, int classes, int min, int max, float angle, float aspect, float hue, float saturation, float exposure, int div) +{ + char **random_paths = get_random_paths(paths, n, m); + int i; + data d = {0}; + d.shallow = 0; + + d.X.rows = n; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + d.X.cols = h*w*3; + + + d.y.rows = n; + d.y.cols = h*w*classes/div/div; + d.y.vals = calloc(d.X.rows, sizeof(float*)); + + for(i = 0; i < n; ++i){ + image orig = load_image_color(random_paths[i], 0, 0); + augment_args a = random_augment_args(orig, angle, aspect, min, max, w, h); + image sized = rotate_crop_image(orig, a.rad, a.scale, a.w, a.h, a.dx, a.dy, a.aspect); + + int flip = rand()%2; + if(flip) flip_image(sized); + random_distort_image(sized, hue, saturation, exposure); + d.X.vals[i] = sized.data; + + image mask = get_segmentation_image(random_paths[i], orig.w, orig.h, classes); + //image mask = make_image(orig.w, orig.h, classes+1); + image sized_m = rotate_crop_image(mask, a.rad, a.scale/div, a.w/div, a.h/div, a.dx/div, a.dy/div, a.aspect); + + if(flip) flip_image(sized_m); + d.y.vals[i] = sized_m.data; + + free_image(orig); + free_image(mask); + + /* + image rgb = mask_to_rgb(sized_m, classes); + show_image(rgb, "part"); + show_image(sized, "orig"); + cvWaitKey(0); + free_image(rgb); + */ + } + free(random_paths); + return d; +} + +data load_data_iseg(int n, char **paths, int m, int w, int h, int classes, int boxes, int div, int min, int max, float angle, float aspect, float hue, float saturation, float exposure) +{ + char **random_paths = get_random_paths(paths, n, m); + int i; + data d = {0}; + d.shallow = 0; + + d.X.rows = n; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + d.X.cols = h*w*3; + + d.y = make_matrix(n, (((w/div)*(h/div))+1)*boxes); + + for(i = 0; i < n; ++i){ + image orig = load_image_color(random_paths[i], 0, 0); + augment_args a = random_augment_args(orig, angle, aspect, min, max, w, h); + image sized = rotate_crop_image(orig, a.rad, a.scale, a.w, a.h, a.dx, a.dy, a.aspect); + + int flip = rand()%2; + if(flip) flip_image(sized); + random_distort_image(sized, hue, saturation, exposure); + d.X.vals[i] = sized.data; + //show_image(sized, "image"); + + fill_truth_iseg(random_paths[i], boxes, d.y.vals[i], classes, orig.w, orig.h, a, flip, w/div, h/div); + + free_image(orig); + + /* + image rgb = mask_to_rgb(sized_m, classes); + show_image(rgb, "part"); + show_image(sized, "orig"); + cvWaitKey(0); + free_image(rgb); + */ + } + free(random_paths); + return d; +} + +data load_data_mask(int n, char **paths, int m, int w, int h, int classes, int boxes, int coords, int min, int max, float angle, float aspect, float hue, float saturation, float exposure) +{ + char **random_paths = get_random_paths(paths, n, m); + int i; + data d = {0}; + d.shallow = 0; + + d.X.rows = n; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + d.X.cols = h*w*3; + + d.y = make_matrix(n, (coords+1)*boxes); + + for(i = 0; i < n; ++i){ + image orig = load_image_color(random_paths[i], 0, 0); + augment_args a = random_augment_args(orig, angle, aspect, min, max, w, h); + image sized = rotate_crop_image(orig, a.rad, a.scale, a.w, a.h, a.dx, a.dy, a.aspect); + + int flip = rand()%2; + if(flip) flip_image(sized); + random_distort_image(sized, hue, saturation, exposure); + d.X.vals[i] = sized.data; + //show_image(sized, "image"); + + fill_truth_mask(random_paths[i], boxes, d.y.vals[i], classes, orig.w, orig.h, a, flip, 14, 14); + + free_image(orig); + + /* + image rgb = mask_to_rgb(sized_m, classes); + show_image(rgb, "part"); + show_image(sized, "orig"); + cvWaitKey(0); + free_image(rgb); + */ + } + free(random_paths); + return d; +} + data load_data_region(int n, char **paths, int m, int w, int h, int size, int classes, float jitter, float hue, float saturation, float exposure) { char **random_paths = get_random_paths(paths, n, m); @@ -624,7 +998,7 @@ data load_data_swag(char **paths, int n, int classes, float jitter) d.X.vals = calloc(d.X.rows, sizeof(float*)); d.X.cols = h*w*3; - int k = (4+classes)*30; + int k = (4+classes)*90; d.y = make_matrix(1, k); int dw = w*jitter; @@ -673,45 +1047,46 @@ data load_data_detection(int n, char **paths, int m, int w, int h, int boxes, in d.y = make_matrix(n, 5*boxes); for(i = 0; i < n; ++i){ image orig = load_image_color(random_paths[i], 0, 0); + image sized = make_image(w, h, orig.c); + fill_image(sized, .5); - int oh = orig.h; - int ow = orig.w; + float dw = jitter * orig.w; + float dh = jitter * orig.h; - int dw = (ow*jitter); - int dh = (oh*jitter); + float new_ar = (orig.w + rand_uniform(-dw, dw)) / (orig.h + rand_uniform(-dh, dh)); + //float scale = rand_uniform(.25, 2); + float scale = 1; - int pleft = rand_uniform(-dw, dw); - int pright = rand_uniform(-dw, dw); - int ptop = rand_uniform(-dh, dh); - int pbot = rand_uniform(-dh, dh); + float nw, nh; - int swidth = ow - pleft - pright; - int sheight = oh - ptop - pbot; + if(new_ar < 1){ + nh = scale * h; + nw = nh * new_ar; + } else { + nw = scale * w; + nh = nw / new_ar; + } - float sx = (float)swidth / ow; - float sy = (float)sheight / oh; + float dx = rand_uniform(0, w - nw); + float dy = rand_uniform(0, h - nh); - int flip = rand()%2; - image cropped = crop_image(orig, pleft, ptop, swidth, sheight); + place_image(orig, nw, nh, dx, dy, sized); - float dx = ((float)pleft/ow)/sx; - float dy = ((float)ptop /oh)/sy; + random_distort_image(sized, hue, saturation, exposure); - image sized = resize_image(cropped, w, h); + int flip = rand()%2; if(flip) flip_image(sized); - random_distort_image(sized, hue, saturation, exposure); d.X.vals[i] = sized.data; - fill_truth_detection(random_paths[i], boxes, d.y.vals[i], classes, flip, dx, dy, 1./sx, 1./sy); + + fill_truth_detection(random_paths[i], boxes, d.y.vals[i], classes, flip, -dx/w, -dy/h, nw/w, nh/h); free_image(orig); - free_image(cropped); } free(random_paths); return d; } - void *load_thread(void *ptr) { //printf("Loading data: %d\n", rand()); @@ -722,12 +1097,20 @@ void *load_thread(void *ptr) if (a.type == OLD_CLASSIFICATION_DATA){ *a.d = load_data_old(a.paths, a.n, a.m, a.labels, a.classes, a.w, a.h); + } else if (a.type == REGRESSION_DATA){ + *a.d = load_data_regression(a.paths, a.n, a.m, a.classes, a.min, a.max, a.size, a.angle, a.aspect, a.hue, a.saturation, a.exposure); } else if (a.type == CLASSIFICATION_DATA){ - *a.d = load_data_augment(a.paths, a.n, a.m, a.labels, a.classes, a.hierarchy, a.min, a.max, a.size, a.angle, a.aspect, a.hue, a.saturation, a.exposure); + *a.d = load_data_augment(a.paths, a.n, a.m, a.labels, a.classes, a.hierarchy, a.min, a.max, a.size, a.angle, a.aspect, a.hue, a.saturation, a.exposure, a.center); } else if (a.type == SUPER_DATA){ *a.d = load_data_super(a.paths, a.n, a.m, a.w, a.h, a.scale); } else if (a.type == WRITING_DATA){ *a.d = load_data_writing(a.paths, a.n, a.m, a.w, a.h, a.out_w, a.out_h); + } else if (a.type == ISEG_DATA){ + *a.d = load_data_iseg(a.n, a.paths, a.m, a.w, a.h, a.classes, a.num_boxes, a.scale, a.min, a.max, a.angle, a.aspect, a.hue, a.saturation, a.exposure); + } else if (a.type == INSTANCE_DATA){ + *a.d = load_data_mask(a.n, a.paths, a.m, a.w, a.h, a.classes, a.num_boxes, a.coords, a.min, a.max, a.angle, a.aspect, a.hue, a.saturation, a.exposure); + } else if (a.type == SEGMENTATION_DATA){ + *a.d = load_data_seg(a.n, a.paths, a.m, a.w, a.h, a.classes, a.min, a.max, a.angle, a.aspect, a.hue, a.saturation, a.exposure, a.scale); } else if (a.type == REGION_DATA){ *a.d = load_data_region(a.n, a.paths, a.m, a.w, a.h, a.num_boxes, a.classes, a.jitter, a.hue, a.saturation, a.exposure); } else if (a.type == DETECTION_DATA){ @@ -739,6 +1122,9 @@ void *load_thread(void *ptr) } else if (a.type == IMAGE_DATA){ *(a.im) = load_image_color(a.path, 0, 0); *(a.resized) = resize_image(*(a.im), a.w, a.h); + } else if (a.type == LETTERBOX_DATA){ + *(a.im) = load_image_color(a.path, 0, 0); + *(a.resized) = letterbox_image(*(a.im), a.w, a.h); } else if (a.type == TAG_DATA){ *a.d = load_data_tag(a.paths, a.n, a.m, a.classes, a.min, a.max, a.size, a.angle, a.aspect, a.hue, a.saturation, a.exposure); } @@ -784,6 +1170,13 @@ void *load_threads(void *ptr) return 0; } +void load_data_blocking(load_args args) +{ + struct load_args *ptr = calloc(1, sizeof(struct load_args)); + *ptr = args; + load_thread(ptr); +} + pthread_t load_data(load_args args) { pthread_t thread; @@ -863,12 +1256,95 @@ data load_data_super(char **paths, int n, int m, int w, int h, int scale) return d; } -data load_data_augment(char **paths, int n, int m, char **labels, int k, tree *hierarchy, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure) +data load_data_regression(char **paths, int n, int m, int k, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure) { if(m) paths = get_random_paths(paths, n, m); data d = {0}; d.shallow = 0; - d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure); + d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure, 0); + d.y = load_regression_labels_paths(paths, n, k); + if(m) free(paths); + return d; +} + +data select_data(data *orig, int *inds) +{ + data d = {0}; + d.shallow = 1; + d.w = orig[0].w; + d.h = orig[0].h; + + d.X.rows = orig[0].X.rows; + d.y.rows = orig[0].X.rows; + + d.X.cols = orig[0].X.cols; + d.y.cols = orig[0].y.cols; + + d.X.vals = calloc(orig[0].X.rows, sizeof(float *)); + d.y.vals = calloc(orig[0].y.rows, sizeof(float *)); + int i; + for(i = 0; i < d.X.rows; ++i){ + d.X.vals[i] = orig[inds[i]].X.vals[i]; + d.y.vals[i] = orig[inds[i]].y.vals[i]; + } + return d; +} + +data *tile_data(data orig, int divs, int size) +{ + data *ds = calloc(divs*divs, sizeof(data)); + int i, j; +#pragma omp parallel for + for(i = 0; i < divs*divs; ++i){ + data d; + d.shallow = 0; + d.w = orig.w/divs * size; + d.h = orig.h/divs * size; + d.X.rows = orig.X.rows; + d.X.cols = d.w*d.h*3; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + + d.y = copy_matrix(orig.y); +#pragma omp parallel for + for(j = 0; j < orig.X.rows; ++j){ + int x = (i%divs) * orig.w / divs - (d.w - orig.w/divs)/2; + int y = (i/divs) * orig.h / divs - (d.h - orig.h/divs)/2; + image im = float_to_image(orig.w, orig.h, 3, orig.X.vals[j]); + d.X.vals[j] = crop_image(im, x, y, d.w, d.h).data; + } + ds[i] = d; + } + return ds; +} + +data resize_data(data orig, int w, int h) +{ + data d = {0}; + d.shallow = 0; + d.w = w; + d.h = h; + int i; + d.X.rows = orig.X.rows; + d.X.cols = w*h*3; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + + d.y = copy_matrix(orig.y); +#pragma omp parallel for + for(i = 0; i < orig.X.rows; ++i){ + image im = float_to_image(orig.w, orig.h, 3, orig.X.vals[i]); + d.X.vals[i] = resize_image(im, w, h).data; + } + return d; +} + +data load_data_augment(char **paths, int n, int m, char **labels, int k, tree *hierarchy, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure, int center) +{ + if(m) paths = get_random_paths(paths, n, m); + data d = {0}; + d.shallow = 0; + d.w=size; + d.h=size; + d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure, center); d.y = load_labels_paths(paths, n, labels, k, hierarchy); if(m) free(paths); return d; @@ -881,7 +1357,7 @@ data load_data_tag(char **paths, int n, int m, int k, int min, int max, int size d.w = size; d.h = size; d.shallow = 0; - d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure); + d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure, 0); d.y = load_tags_paths(paths, n, k); if(m) free(paths); return d; @@ -909,6 +1385,8 @@ data concat_data(data d1, data d2) d.shallow = 1; d.X = concat_matrix(d1.X, d2.X); d.y = concat_matrix(d1.y, d2.y); + d.w = d1.w; + d.h = d1.h; return d; } @@ -962,7 +1440,6 @@ data load_cifar10_data(char *filename) X.vals[i][j] = (double)bytes[j+1]; } } - //translate_data_rows(d, -128); scale_data_rows(d, 1./255); //normalize_data_rows(d); fclose(fp); @@ -985,7 +1462,7 @@ void get_next_batch(data d, int n, int offset, float *X, float *y) for(j = 0; j < n; ++j){ int index = offset + j; memcpy(X+j*d.X.cols, d.X.vals[index], d.X.cols*sizeof(float)); - memcpy(y+j*d.y.cols, d.y.vals[index], d.y.cols*sizeof(float)); + if(y) memcpy(y+j*d.y.cols, d.y.vals[index], d.y.cols*sizeof(float)); } } @@ -1029,7 +1506,6 @@ data load_all_cifar10() fclose(fp); } //normalize_data_rows(d); - //translate_data_rows(d, -128); scale_data_rows(d, 1./255); smooth_data(d); return d; @@ -1113,6 +1589,19 @@ void translate_data_rows(data d, float s) } } +data copy_data(data d) +{ + data c = {0}; + c.w = d.w; + c.h = d.h; + c.shallow = 0; + c.num_boxes = d.num_boxes; + c.boxes = d.boxes; + c.X = copy_matrix(d.X); + c.y = copy_matrix(d.y); + return c; +} + void normalize_data_rows(data d) { int i; diff --git a/image.darknet/src/data.h b/image.darknet/src/data.h index 3f6ef61..781906f 100644 --- a/image.darknet/src/data.h +++ b/image.darknet/src/data.h @@ -2,6 +2,7 @@ #define DATA_H #include +#include "darknet.h" #include "matrix.h" #include "list.h" #include "image.h" @@ -17,93 +18,32 @@ static inline float distance_from_edge(int x, int max) if (dist > 1) dist = 1; return dist; } +void load_data_blocking(load_args args); -typedef struct{ - int w, h; - matrix X; - matrix y; - int shallow; - int *num_boxes; - box **boxes; -} data; - -typedef enum { - CLASSIFICATION_DATA, DETECTION_DATA, CAPTCHA_DATA, REGION_DATA, IMAGE_DATA, COMPARE_DATA, WRITING_DATA, SWAG_DATA, TAG_DATA, OLD_CLASSIFICATION_DATA, STUDY_DATA, DET_DATA, SUPER_DATA -} data_type; - -typedef struct load_args{ - int threads; - char **paths; - char *path; - int n; - int m; - char **labels; - int h; - int w; - int out_w; - int out_h; - int nh; - int nw; - int num_boxes; - int min, max, size; - int classes; - int background; - int scale; - float jitter; - float angle; - float aspect; - float saturation; - float exposure; - float hue; - data *d; - image *im; - image *resized; - data_type type; - tree *hierarchy; -} load_args; - -typedef struct{ - int id; - float x,y,w,h; - float left, right, top, bottom; -} box_label; - -void free_data(data d); - -pthread_t load_data(load_args args); - -pthread_t load_data_in_thread(load_args args); void print_letters(float *pred, int n); data load_data_captcha(char **paths, int n, int m, int k, int w, int h); data load_data_captcha_encode(char **paths, int n, int m, int w, int h); -data load_data_old(char **paths, int n, int m, char **labels, int k, int w, int h); data load_data_detection(int n, char **paths, int m, int w, int h, int boxes, int classes, float jitter, float hue, float saturation, float exposure); data load_data_tag(char **paths, int n, int m, int k, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure); -matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure); +matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure, int center); data load_data_super(char **paths, int n, int m, int w, int h, int scale); -data load_data_augment(char **paths, int n, int m, char **labels, int k, tree *hierarchy, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure); +data load_data_augment(char **paths, int n, int m, char **labels, int k, tree *hierarchy, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure, int center); +data load_data_regression(char **paths, int n, int m, int classes, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure); data load_go(char *filename); -box_label *read_boxes(char *filename, int *n); -data load_cifar10_data(char *filename); -data load_all_cifar10(); data load_data_writing(char **paths, int n, int m, int w, int h, int out_w, int out_h); -list *get_paths(char *filename); -char **get_labels(char *filename); void get_random_batch(data d, int n, float *X, float *y); data get_data_part(data d, int part, int total); data get_random_data(data d, int num); -void get_next_batch(data d, int n, int offset, float *X, float *y); data load_categorical_data_csv(char *filename, int target, int k); void normalize_data_rows(data d); void scale_data_rows(data d, float s); void translate_data_rows(data d, float s); void randomize_data(data d); data *split_data(data d, int part, int total); -data concat_data(data d1, data d2); data concat_datas(data *d, int n); void fill_truth(char *path, char **labels, int k, float *truth); diff --git a/image.darknet/src/deconvolutional_kernels.cu b/image.darknet/src/deconvolutional_kernels.cu index d6259fb..8267dcf 100644 --- a/image.darknet/src/deconvolutional_kernels.cu +++ b/image.darknet/src/deconvolutional_kernels.cu @@ -5,6 +5,7 @@ extern "C" { #include "convolutional_layer.h" #include "deconvolutional_layer.h" +#include "batchnorm_layer.h" #include "gemm.h" #include "blas.h" #include "im2col.h" @@ -13,97 +14,126 @@ extern "C" { #include "cuda.h" } -extern "C" void forward_deconvolutional_layer_gpu(deconvolutional_layer layer, network_state state) +extern "C" void forward_deconvolutional_layer_gpu(layer l, network net) { int i; - int out_h = deconvolutional_out_height(layer); - int out_w = deconvolutional_out_width(layer); - int size = out_h*out_w; - int m = layer.size*layer.size*layer.n; - int n = layer.h*layer.w; - int k = layer.c; + int m = l.size*l.size*l.n; + int n = l.h*l.w; + int k = l.c; - fill_ongpu(layer.outputs*layer.batch, 0, layer.output_gpu, 1); + fill_gpu(l.outputs*l.batch, 0, l.output_gpu, 1); - for(i = 0; i < layer.batch; ++i){ - float *a = layer.weights_gpu; - float *b = state.input + i*layer.c*layer.h*layer.w; - float *c = layer.col_image_gpu; + for(i = 0; i < l.batch; ++i){ + float *a = l.weights_gpu; + float *b = net.input_gpu + i*l.c*l.h*l.w; + float *c = net.workspace; - gemm_ongpu(1,0,m,n,k,1,a,m,b,n,0,c,n); + gemm_gpu(1,0,m,n,k,1,a,m,b,n,0,c,n); - col2im_ongpu(c, layer.n, out_h, out_w, layer.size, layer.stride, 0, layer.output_gpu+i*layer.n*size); + col2im_gpu(net.workspace, l.out_c, l.out_h, l.out_w, l.size, l.stride, l.pad, l.output_gpu+i*l.outputs); } - add_bias_gpu(layer.output_gpu, layer.biases_gpu, layer.batch, layer.n, size); - activate_array(layer.output_gpu, layer.batch*layer.n*size, layer.activation); + if (l.batch_normalize) { + forward_batchnorm_layer_gpu(l, net); + } else { + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.n, l.out_w*l.out_h); + } + activate_array_gpu(l.output_gpu, l.batch*l.n*l.out_w*l.out_h, l.activation); } -extern "C" void backward_deconvolutional_layer_gpu(deconvolutional_layer layer, network_state state) +extern "C" void backward_deconvolutional_layer_gpu(layer l, network net) { - float alpha = 1./layer.batch; - int out_h = deconvolutional_out_height(layer); - int out_w = deconvolutional_out_width(layer); - int size = out_h*out_w; int i; - gradient_array(layer.output_gpu, size*layer.n*layer.batch, layer.activation, layer.delta_gpu); - backward_bias(layer.bias_updates_gpu, layer.delta, layer.batch, layer.n, size); + //constrain_gpu(l.outputs*l.batch, 1, l.delta_gpu, 1); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + + if(l.batch_normalize){ + backward_batchnorm_layer_gpu(l, net); + } else { + backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.n, l.out_w*l.out_h); + } - if(state.delta) memset(state.delta, 0, layer.batch*layer.h*layer.w*layer.c*sizeof(float)); + //if(net.delta_gpu) memset(net.delta_gpu, 0, l.batch*l.h*l.w*l.c*sizeof(float)); - for(i = 0; i < layer.batch; ++i){ - int m = layer.c; - int n = layer.size*layer.size*layer.n; - int k = layer.h*layer.w; + for(i = 0; i < l.batch; ++i){ + int m = l.c; + int n = l.size*l.size*l.n; + int k = l.h*l.w; - float *a = state.input + i*m*n; - float *b = layer.col_image_gpu; - float *c = layer.weight_updates_gpu; + float *a = net.input_gpu + i*m*k; + float *b = net.workspace; + float *c = l.weight_updates_gpu; - im2col_ongpu(layer.delta_gpu + i*layer.n*size, layer.n, out_h, out_w, - layer.size, layer.stride, 0, b); - gemm_ongpu(0,1,m,n,k,alpha,a,k,b,k,1,c,n); + im2col_gpu(l.delta_gpu + i*l.outputs, l.out_c, l.out_h, l.out_w, + l.size, l.stride, l.pad, b); + gemm_gpu(0,1,m,n,k,1,a,k,b,k,1,c,n); - if(state.delta){ - int m = layer.c; - int n = layer.h*layer.w; - int k = layer.size*layer.size*layer.n; + if(net.delta_gpu){ + int m = l.c; + int n = l.h*l.w; + int k = l.size*l.size*l.n; - float *a = layer.weights_gpu; - float *b = layer.col_image_gpu; - float *c = state.delta + i*n*m; + float *a = l.weights_gpu; + float *b = net.workspace; + float *c = net.delta_gpu + i*n*m; - gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); + gemm_gpu(0,0,m,n,k,1,a,k,b,n,1,c,n); } } } -extern "C" void pull_deconvolutional_layer(deconvolutional_layer layer) +extern "C" void pull_deconvolutional_layer(layer l) { - cuda_pull_array(layer.weights_gpu, layer.weights, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.biases_gpu, layer.biases, layer.n); - cuda_pull_array(layer.weight_updates_gpu, layer.weight_updates, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.bias_updates_gpu, layer.bias_updates, layer.n); + cuda_pull_array(l.weights_gpu, l.weights, l.c*l.n*l.size*l.size); + cuda_pull_array(l.biases_gpu, l.biases, l.n); + cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.c*l.n*l.size*l.size); + cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.n); + if (l.batch_normalize){ + cuda_pull_array(l.scales_gpu, l.scales, l.n); + cuda_pull_array(l.rolling_mean_gpu, l.rolling_mean, l.n); + cuda_pull_array(l.rolling_variance_gpu, l.rolling_variance, l.n); + } } -extern "C" void push_deconvolutional_layer(deconvolutional_layer layer) +extern "C" void push_deconvolutional_layer(layer l) { - cuda_push_array(layer.weights_gpu, layer.weights, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.biases_gpu, layer.biases, layer.n); - cuda_push_array(layer.weight_updates_gpu, layer.weight_updates, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.bias_updates_gpu, layer.bias_updates, layer.n); + cuda_push_array(l.weights_gpu, l.weights, l.c*l.n*l.size*l.size); + cuda_push_array(l.biases_gpu, l.biases, l.n); + cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.c*l.n*l.size*l.size); + cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.n); + if (l.batch_normalize){ + cuda_push_array(l.scales_gpu, l.scales, l.n); + cuda_push_array(l.rolling_mean_gpu, l.rolling_mean, l.n); + cuda_push_array(l.rolling_variance_gpu, l.rolling_variance, l.n); + } } -extern "C" void update_deconvolutional_layer_gpu(deconvolutional_layer layer, float learning_rate, float momentum, float decay) +void update_deconvolutional_layer_gpu(layer l, update_args a) { - int size = layer.size*layer.size*layer.c*layer.n; + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + + if(a.adam){ + adam_update_gpu(l.weights_gpu, l.weight_updates_gpu, l.m_gpu, l.v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.nweights, batch, a.t); + adam_update_gpu(l.biases_gpu, l.bias_updates_gpu, l.bias_m_gpu, l.bias_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.n, batch, a.t); + if(l.scales_gpu){ + adam_update_gpu(l.scales_gpu, l.scale_updates_gpu, l.scale_m_gpu, l.scale_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.n, batch, a.t); + } + }else{ + axpy_gpu(l.nweights, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); + axpy_gpu(l.nweights, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); + scal_gpu(l.nweights, momentum, l.weight_updates_gpu, 1); - axpy_ongpu(layer.n, learning_rate, layer.bias_updates_gpu, 1, layer.biases_gpu, 1); - scal_ongpu(layer.n, momentum, layer.bias_updates_gpu, 1); + axpy_gpu(l.n, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); + scal_gpu(l.n, momentum, l.bias_updates_gpu, 1); - axpy_ongpu(size, -decay, layer.weights_gpu, 1, layer.weight_updates_gpu, 1); - axpy_ongpu(size, learning_rate, layer.weight_updates_gpu, 1, layer.weights_gpu, 1); - scal_ongpu(size, momentum, layer.weight_updates_gpu, 1); + if(l.scales_gpu){ + axpy_gpu(l.n, learning_rate/batch, l.scale_updates_gpu, 1, l.scales_gpu, 1); + scal_gpu(l.n, momentum, l.scale_updates_gpu, 1); + } + } } diff --git a/image.darknet/src/deconvolutional_layer.c b/image.darknet/src/deconvolutional_layer.c index fbef9d5..00c0e85 100644 --- a/image.darknet/src/deconvolutional_layer.c +++ b/image.darknet/src/deconvolutional_layer.c @@ -1,52 +1,41 @@ #include "deconvolutional_layer.h" #include "convolutional_layer.h" +#include "batchnorm_layer.h" #include "utils.h" #include "im2col.h" #include "col2im.h" #include "blas.h" #include "gemm.h" + #include #include -int deconvolutional_out_height(deconvolutional_layer l) -{ - int h = l.stride*(l.h - 1) + l.size; - return h; -} -int deconvolutional_out_width(deconvolutional_layer l) -{ - int w = l.stride*(l.w - 1) + l.size; - return w; -} - -int deconvolutional_out_size(deconvolutional_layer l) -{ - return deconvolutional_out_height(l) * deconvolutional_out_width(l); +static size_t get_workspace_size(layer l){ + return (size_t)l.h*l.w*l.size*l.size*l.n*sizeof(float); } -image get_deconvolutional_image(deconvolutional_layer l) +void bilinear_init(layer l) { - int h,w,c; - h = deconvolutional_out_height(l); - w = deconvolutional_out_width(l); - c = l.n; - return float_to_image(w,h,c,l.output); + int i,j,f; + float center = (l.size-1) / 2.; + for(f = 0; f < l.n; ++f){ + for(j = 0; j < l.size; ++j){ + for(i = 0; i < l.size; ++i){ + float val = (1 - fabs(i - center)) * (1 - fabs(j - center)); + int c = f%l.c; + int ind = f*l.size*l.size*l.c + c*l.size*l.size + j*l.size + i; + l.weights[ind] = val; + } + } + } } -image get_deconvolutional_delta(deconvolutional_layer l) -{ - int h,w,c; - h = deconvolutional_out_height(l); - w = deconvolutional_out_width(l); - c = l.n; - return float_to_image(w,h,c,l.delta); -} -deconvolutional_layer make_deconvolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, ACTIVATION activation) +layer make_deconvolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int adam) { int i; - deconvolutional_layer l = {0}; + layer l = {0}; l.type = DECONVOLUTIONAL; l.h = h; @@ -57,82 +46,182 @@ deconvolutional_layer make_deconvolutional_layer(int batch, int h, int w, int c, l.stride = stride; l.size = size; + l.nweights = c*n*size*size; + l.nbiases = n; + l.weights = calloc(c*n*size*size, sizeof(float)); l.weight_updates = calloc(c*n*size*size, sizeof(float)); l.biases = calloc(n, sizeof(float)); l.bias_updates = calloc(n, sizeof(float)); - float scale = 1./sqrt(size*size*c); + //float scale = n/(size*size*c); + //printf("scale: %f\n", scale); + float scale = .02; for(i = 0; i < c*n*size*size; ++i) l.weights[i] = scale*rand_normal(); + //bilinear_init(l); for(i = 0; i < n; ++i){ - l.biases[i] = scale; + l.biases[i] = 0; } - int out_h = deconvolutional_out_height(l); - int out_w = deconvolutional_out_width(l); + l.pad = padding; - l.out_h = out_h; - l.out_w = out_w; + l.out_h = (l.h - 1) * l.stride + l.size - 2*l.pad; + l.out_w = (l.w - 1) * l.stride + l.size - 2*l.pad; l.out_c = n; l.outputs = l.out_w * l.out_h * l.out_c; l.inputs = l.w * l.h * l.c; - l.col_image = calloc(h*w*size*size*n, sizeof(float)); - l.output = calloc(l.batch*out_h * out_w * n, sizeof(float)); - l.delta = calloc(l.batch*out_h * out_w * n, sizeof(float)); + scal_cpu(l.nweights, (float)l.out_w*l.out_h/(l.w*l.h), l.weights, 1); + + l.output = calloc(l.batch*l.outputs, sizeof(float)); + l.delta = calloc(l.batch*l.outputs, sizeof(float)); l.forward = forward_deconvolutional_layer; l.backward = backward_deconvolutional_layer; l.update = update_deconvolutional_layer; - #ifdef GPU - l.weights_gpu = cuda_make_array(l.weights, c*n*size*size); - l.weight_updates_gpu = cuda_make_array(l.weight_updates, c*n*size*size); + l.batch_normalize = batch_normalize; + + if(batch_normalize){ + l.scales = calloc(n, sizeof(float)); + l.scale_updates = calloc(n, sizeof(float)); + for(i = 0; i < n; ++i){ + l.scales[i] = 1; + } + + l.mean = calloc(n, sizeof(float)); + l.variance = calloc(n, sizeof(float)); + + l.mean_delta = calloc(n, sizeof(float)); + l.variance_delta = calloc(n, sizeof(float)); + + l.rolling_mean = calloc(n, sizeof(float)); + l.rolling_variance = calloc(n, sizeof(float)); + l.x = calloc(l.batch*l.outputs, sizeof(float)); + l.x_norm = calloc(l.batch*l.outputs, sizeof(float)); + } + if(adam){ + l.m = calloc(c*n*size*size, sizeof(float)); + l.v = calloc(c*n*size*size, sizeof(float)); + l.bias_m = calloc(n, sizeof(float)); + l.scale_m = calloc(n, sizeof(float)); + l.bias_v = calloc(n, sizeof(float)); + l.scale_v = calloc(n, sizeof(float)); + } + +#ifdef GPU + l.forward_gpu = forward_deconvolutional_layer_gpu; + l.backward_gpu = backward_deconvolutional_layer_gpu; + l.update_gpu = update_deconvolutional_layer_gpu; + + if(gpu_index >= 0){ + + if (adam) { + l.m_gpu = cuda_make_array(l.m, c*n*size*size); + l.v_gpu = cuda_make_array(l.v, c*n*size*size); + l.bias_m_gpu = cuda_make_array(l.bias_m, n); + l.bias_v_gpu = cuda_make_array(l.bias_v, n); + l.scale_m_gpu = cuda_make_array(l.scale_m, n); + l.scale_v_gpu = cuda_make_array(l.scale_v, n); + } + l.weights_gpu = cuda_make_array(l.weights, c*n*size*size); + l.weight_updates_gpu = cuda_make_array(l.weight_updates, c*n*size*size); + + l.biases_gpu = cuda_make_array(l.biases, n); + l.bias_updates_gpu = cuda_make_array(l.bias_updates, n); - l.biases_gpu = cuda_make_array(l.biases, n); - l.bias_updates_gpu = cuda_make_array(l.bias_updates, n); + l.delta_gpu = cuda_make_array(l.delta, l.batch*l.out_h*l.out_w*n); + l.output_gpu = cuda_make_array(l.output, l.batch*l.out_h*l.out_w*n); - l.col_image_gpu = cuda_make_array(l.col_image, h*w*size*size*n); - l.delta_gpu = cuda_make_array(l.delta, l.batch*out_h*out_w*n); - l.output_gpu = cuda_make_array(l.output, l.batch*out_h*out_w*n); + if(batch_normalize){ + l.mean_gpu = cuda_make_array(0, n); + l.variance_gpu = cuda_make_array(0, n); + + l.rolling_mean_gpu = cuda_make_array(0, n); + l.rolling_variance_gpu = cuda_make_array(0, n); + + l.mean_delta_gpu = cuda_make_array(0, n); + l.variance_delta_gpu = cuda_make_array(0, n); + + l.scales_gpu = cuda_make_array(l.scales, n); + l.scale_updates_gpu = cuda_make_array(0, n); + + l.x_gpu = cuda_make_array(0, l.batch*l.out_h*l.out_w*n); + l.x_norm_gpu = cuda_make_array(0, l.batch*l.out_h*l.out_w*n); + } + } + #ifdef CUDNN + cudnnCreateTensorDescriptor(&l.dstTensorDesc); + cudnnCreateTensorDescriptor(&l.normTensorDesc); + cudnnSetTensor4dDescriptor(l.dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l.batch, l.out_c, l.out_h, l.out_w); + cudnnSetTensor4dDescriptor(l.normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l.out_c, 1, 1); #endif +#endif l.activation = activation; + l.workspace_size = get_workspace_size(l); - fprintf(stderr, "Deconvolutional Layer: %d x %d x %d image, %d filters -> %d x %d x %d image\n", h,w,c,n, out_h, out_w, n); + fprintf(stderr, "deconv%5d %2d x%2d /%2d %4d x%4d x%4d -> %4d x%4d x%4d\n", n, size, size, stride, w, h, c, l.out_w, l.out_h, l.out_c); return l; } -void resize_deconvolutional_layer(deconvolutional_layer *l, int h, int w) +void denormalize_deconvolutional_layer(layer l) +{ + int i, j; + for(i = 0; i < l.n; ++i){ + float scale = l.scales[i]/sqrt(l.rolling_variance[i] + .00001); + for(j = 0; j < l.c*l.size*l.size; ++j){ + l.weights[i*l.c*l.size*l.size + j] *= scale; + } + l.biases[i] -= l.rolling_mean[i] * scale; + l.scales[i] = 1; + l.rolling_mean[i] = 0; + l.rolling_variance[i] = 1; + } +} + +void resize_deconvolutional_layer(layer *l, int h, int w) { l->h = h; l->w = w; - int out_h = deconvolutional_out_height(*l); - int out_w = deconvolutional_out_width(*l); - - l->col_image = realloc(l->col_image, - out_h*out_w*l->size*l->size*l->c*sizeof(float)); - l->output = realloc(l->output, - l->batch*out_h * out_w * l->n*sizeof(float)); - l->delta = realloc(l->delta, - l->batch*out_h * out_w * l->n*sizeof(float)); - #ifdef GPU - cuda_free(l->col_image_gpu); + l->out_h = (l->h - 1) * l->stride + l->size - 2*l->pad; + l->out_w = (l->w - 1) * l->stride + l->size - 2*l->pad; + + l->outputs = l->out_h * l->out_w * l->out_c; + l->inputs = l->w * l->h * l->c; + + l->output = realloc(l->output, l->batch*l->outputs*sizeof(float)); + l->delta = realloc(l->delta, l->batch*l->outputs*sizeof(float)); + if(l->batch_normalize){ + l->x = realloc(l->x, l->batch*l->outputs*sizeof(float)); + l->x_norm = realloc(l->x_norm, l->batch*l->outputs*sizeof(float)); + } + +#ifdef GPU cuda_free(l->delta_gpu); cuda_free(l->output_gpu); - l->col_image_gpu = cuda_make_array(l->col_image, out_h*out_w*l->size*l->size*l->c); - l->delta_gpu = cuda_make_array(l->delta, l->batch*out_h*out_w*l->n); - l->output_gpu = cuda_make_array(l->output, l->batch*out_h*out_w*l->n); + l->delta_gpu = cuda_make_array(l->delta, l->batch*l->outputs); + l->output_gpu = cuda_make_array(l->output, l->batch*l->outputs); + + if(l->batch_normalize){ + cuda_free(l->x_gpu); + cuda_free(l->x_norm_gpu); + + l->x_gpu = cuda_make_array(l->output, l->batch*l->outputs); + l->x_norm_gpu = cuda_make_array(l->output, l->batch*l->outputs); + } + #ifdef CUDNN + cudnnSetTensor4dDescriptor(l->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->out_c, l->out_h, l->out_w); + cudnnSetTensor4dDescriptor(l->normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l->out_c, 1, 1); #endif +#endif + l->workspace_size = get_workspace_size(*l); } -void forward_deconvolutional_layer(const deconvolutional_layer l, network_state state) +void forward_deconvolutional_layer(const layer l, network net) { int i; - int out_h = deconvolutional_out_height(l); - int out_w = deconvolutional_out_width(l); - int size = out_h*out_w; int m = l.size*l.size*l.n; int n = l.h*l.w; @@ -142,63 +231,80 @@ void forward_deconvolutional_layer(const deconvolutional_layer l, network_state for(i = 0; i < l.batch; ++i){ float *a = l.weights; - float *b = state.input + i*l.c*l.h*l.w; - float *c = l.col_image; + float *b = net.input + i*l.c*l.h*l.w; + float *c = net.workspace; - gemm(1,0,m,n,k,1,a,m,b,n,0,c,n); + gemm_cpu(1,0,m,n,k,1,a,m,b,n,0,c,n); - col2im_cpu(c, l.n, out_h, out_w, l.size, l.stride, 0, l.output+i*l.n*size); + col2im_cpu(net.workspace, l.out_c, l.out_h, l.out_w, l.size, l.stride, l.pad, l.output+i*l.outputs); + } + if (l.batch_normalize) { + forward_batchnorm_layer(l, net); + } else { + add_bias(l.output, l.biases, l.batch, l.n, l.out_w*l.out_h); } - add_bias(l.output, l.biases, l.batch, l.n, size); - activate_array(l.output, l.batch*l.n*size, l.activation); + activate_array(l.output, l.batch*l.n*l.out_w*l.out_h, l.activation); } -void backward_deconvolutional_layer(deconvolutional_layer l, network_state state) +void backward_deconvolutional_layer(layer l, network net) { - float alpha = 1./l.batch; - int out_h = deconvolutional_out_height(l); - int out_w = deconvolutional_out_width(l); - int size = out_h*out_w; int i; - gradient_array(l.output, size*l.n*l.batch, l.activation, l.delta); - backward_bias(l.bias_updates, l.delta, l.batch, l.n, size); + gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); + + if(l.batch_normalize){ + backward_batchnorm_layer(l, net); + } else { + backward_bias(l.bias_updates, l.delta, l.batch, l.n, l.out_w*l.out_h); + } + + //if(net.delta) memset(net.delta, 0, l.batch*l.h*l.w*l.c*sizeof(float)); for(i = 0; i < l.batch; ++i){ int m = l.c; int n = l.size*l.size*l.n; int k = l.h*l.w; - float *a = state.input + i*m*n; - float *b = l.col_image; + float *a = net.input + i*m*k; + float *b = net.workspace; float *c = l.weight_updates; - im2col_cpu(l.delta + i*l.n*size, l.n, out_h, out_w, - l.size, l.stride, 0, b); - gemm(0,1,m,n,k,alpha,a,k,b,k,1,c,n); + im2col_cpu(l.delta + i*l.outputs, l.out_c, l.out_h, l.out_w, + l.size, l.stride, l.pad, b); + gemm_cpu(0,1,m,n,k,1,a,k,b,k,1,c,n); - if(state.delta){ + if(net.delta){ int m = l.c; int n = l.h*l.w; int k = l.size*l.size*l.n; float *a = l.weights; - float *b = l.col_image; - float *c = state.delta + i*n*m; + float *b = net.workspace; + float *c = net.delta + i*n*m; - gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); + gemm_cpu(0,0,m,n,k,1,a,k,b,n,1,c,n); } } } -void update_deconvolutional_layer(deconvolutional_layer l, float learning_rate, float momentum, float decay) +void update_deconvolutional_layer(layer l, update_args a) { + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + int size = l.size*l.size*l.c*l.n; - axpy_cpu(l.n, learning_rate, l.bias_updates, 1, l.biases, 1); + axpy_cpu(l.n, learning_rate/batch, l.bias_updates, 1, l.biases, 1); scal_cpu(l.n, momentum, l.bias_updates, 1); - axpy_cpu(size, -decay, l.weights, 1, l.weight_updates, 1); - axpy_cpu(size, learning_rate, l.weight_updates, 1, l.weights, 1); + if(l.scales){ + axpy_cpu(l.n, learning_rate/batch, l.scale_updates, 1, l.scales, 1); + scal_cpu(l.n, momentum, l.scale_updates, 1); + } + + axpy_cpu(size, -decay*batch, l.weights, 1, l.weight_updates, 1); + axpy_cpu(size, learning_rate/batch, l.weight_updates, 1, l.weights, 1); scal_cpu(size, momentum, l.weight_updates, 1); } diff --git a/image.darknet/src/deconvolutional_layer.h b/image.darknet/src/deconvolutional_layer.h index 2d36e02..b254fb9 100644 --- a/image.darknet/src/deconvolutional_layer.h +++ b/image.darknet/src/deconvolutional_layer.h @@ -7,28 +7,19 @@ #include "layer.h" #include "network.h" -typedef layer deconvolutional_layer; - #ifdef GPU -void forward_deconvolutional_layer_gpu(deconvolutional_layer layer, network_state state); -void backward_deconvolutional_layer_gpu(deconvolutional_layer layer, network_state state); -void update_deconvolutional_layer_gpu(deconvolutional_layer layer, float learning_rate, float momentum, float decay); -void push_deconvolutional_layer(deconvolutional_layer layer); -void pull_deconvolutional_layer(deconvolutional_layer layer); +void forward_deconvolutional_layer_gpu(layer l, network net); +void backward_deconvolutional_layer_gpu(layer l, network net); +void update_deconvolutional_layer_gpu(layer l, update_args a); +void push_deconvolutional_layer(layer l); +void pull_deconvolutional_layer(layer l); #endif -deconvolutional_layer make_deconvolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, ACTIVATION activation); -void resize_deconvolutional_layer(deconvolutional_layer *layer, int h, int w); -void forward_deconvolutional_layer(const deconvolutional_layer layer, network_state state); -void update_deconvolutional_layer(deconvolutional_layer layer, float learning_rate, float momentum, float decay); -void backward_deconvolutional_layer(deconvolutional_layer layer, network_state state); - -image get_deconvolutional_image(deconvolutional_layer layer); -image get_deconvolutional_delta(deconvolutional_layer layer); -image get_deconvolutional_filter(deconvolutional_layer layer, int i); - -int deconvolutional_out_height(deconvolutional_layer layer); -int deconvolutional_out_width(deconvolutional_layer layer); +layer make_deconvolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int adam); +void resize_deconvolutional_layer(layer *l, int h, int w); +void forward_deconvolutional_layer(const layer l, network net); +void update_deconvolutional_layer(layer l, update_args a); +void backward_deconvolutional_layer(layer l, network net); #endif diff --git a/image.darknet/src/demo.c b/image.darknet/src/demo.c index 7818bc3..b89efb8 100644 --- a/image.darknet/src/demo.c +++ b/image.darknet/src/demo.c @@ -9,213 +9,339 @@ #include "demo.h" #include -#define FRAMES 3 +#define DEMO 1 #ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#include "opencv2/imgproc/imgproc_c.h" -image get_image_from_stream(CvCapture *cap); static char **demo_names; static image **demo_alphabet; static int demo_classes; -static float **probs; -static box *boxes; -static network net; -static image in ; -static image in_s ; -static image det ; -static image det_s; -static image disp = {0}; -static CvCapture * cap; +static network *net; +static image buff [3]; +static image buff_letter[3]; +static int buff_index = 0; +static void * cap; static float fps = 0; static float demo_thresh = 0; -static float demo_hier_thresh = .5; +static float demo_hier = .5; +static int running = 0; -static float *predictions[FRAMES]; +static int demo_frame = 3; static int demo_index = 0; -static image images[FRAMES]; +static float **predictions; static float *avg; +static int demo_done = 0; +static int demo_total = 0; +double demo_time; -void *fetch_in_thread(void *ptr) +detection *get_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, int *num); + +int size_network(network *net) { - in = get_image_from_stream(cap); - if(!in.data){ - error("Stream closed."); + int i; + int count = 0; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; + if(l.type == YOLO || l.type == REGION || l.type == DETECTION){ + count += l.outputs; + } } - in_s = resize_image(in, net.w, net.h); - return 0; + return count; +} + +void remember_network(network *net) +{ + int i; + int count = 0; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; + if(l.type == YOLO || l.type == REGION || l.type == DETECTION){ + memcpy(predictions[demo_index] + count, net->layers[i].output, sizeof(float) * l.outputs); + count += l.outputs; + } + } +} + +detection *avg_predictions(network *net, int *nboxes) +{ + int i, j; + int count = 0; + fill_cpu(demo_total, 0, avg, 1); + for(j = 0; j < demo_frame; ++j){ + axpy_cpu(demo_total, 1./demo_frame, predictions[j], 1, avg, 1); + } + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; + if(l.type == YOLO || l.type == REGION || l.type == DETECTION){ + memcpy(l.output, avg + count, sizeof(float) * l.outputs); + count += l.outputs; + } + } + detection *dets = get_network_boxes(net, buff[0].w, buff[0].h, demo_thresh, demo_hier, 0, 1, nboxes); + return dets; } void *detect_in_thread(void *ptr) { + running = 1; float nms = .4; - layer l = net.layers[net.n-1]; - float *X = det_s.data; - float *prediction = network_predict(net, X); - - memcpy(predictions[demo_index], prediction, l.outputs*sizeof(float)); - mean_arrays(predictions, FRAMES, l.outputs, avg); - l.output = avg; - - free_image(det_s); - if(l.type == DETECTION){ - get_detection_boxes(l, 1, 1, demo_thresh, probs, boxes, 0); - } else if (l.type == REGION){ - get_region_boxes(l, 1, 1, demo_thresh, probs, boxes, 0, 0, demo_hier_thresh); - } else { - error("Last layer must produce detections\n"); + layer l = net->layers[net->n-1]; + float *X = buff_letter[(buff_index+2)%3].data; + network_predict(net, X); + + /* + if(l.type == DETECTION){ + get_detection_boxes(l, 1, 1, demo_thresh, probs, boxes, 0); + } else */ + remember_network(net); + detection *dets = 0; + int nboxes = 0; + dets = avg_predictions(net, &nboxes); + + + /* + int i,j; + box zero = {0}; + int classes = l.classes; + for(i = 0; i < demo_detections; ++i){ + avg[i].objectness = 0; + avg[i].bbox = zero; + memset(avg[i].prob, 0, classes*sizeof(float)); + for(j = 0; j < demo_frame; ++j){ + axpy_cpu(classes, 1./demo_frame, dets[j][i].prob, 1, avg[i].prob, 1); + avg[i].objectness += dets[j][i].objectness * 1./demo_frame; + avg[i].bbox.x += dets[j][i].bbox.x * 1./demo_frame; + avg[i].bbox.y += dets[j][i].bbox.y * 1./demo_frame; + avg[i].bbox.w += dets[j][i].bbox.w * 1./demo_frame; + avg[i].bbox.h += dets[j][i].bbox.h * 1./demo_frame; + } + //copy_cpu(classes, dets[0][i].prob, 1, avg[i].prob, 1); + //avg[i].objectness = dets[0][i].objectness; } - if (nms > 0) do_nms(boxes, probs, l.w*l.h*l.n, l.classes, nms); + */ + + if (nms > 0) do_nms_obj(dets, nboxes, l.classes, nms); + printf("\033[2J"); printf("\033[1;1H"); printf("\nFPS:%.1f\n",fps); printf("Objects:\n\n"); + image display = buff[(buff_index+2) % 3]; + draw_detections(display, dets, nboxes, demo_thresh, demo_names, demo_alphabet, demo_classes); + free_detections(dets, nboxes); - images[demo_index] = det; - det = images[(demo_index + FRAMES/2 + 1)%FRAMES]; - demo_index = (demo_index + 1)%FRAMES; - - draw_detections(det, l.w*l.h*l.n, demo_thresh, boxes, probs, demo_names, demo_alphabet, demo_classes); + demo_index = (demo_index + 1)%demo_frame; + running = 0; + return 0; +} +void *fetch_in_thread(void *ptr) +{ + free_image(buff[buff_index]); + buff[buff_index] = get_image_from_stream(cap); + if(buff[buff_index].data == 0) { + demo_done = 1; + return 0; + } + letterbox_image_into(buff[buff_index], net->w, net->h, buff_letter[buff_index]); return 0; } -double get_wall_time() +void *display_in_thread(void *ptr) { - struct timeval time; - if (gettimeofday(&time,NULL)){ + int c = show_image(buff[(buff_index + 1)%3], "Demo", 1); + if (c != -1) c = c%256; + if (c == 27) { + demo_done = 1; return 0; + } else if (c == 82) { + demo_thresh += .02; + } else if (c == 84) { + demo_thresh -= .02; + if(demo_thresh <= .02) demo_thresh = .02; + } else if (c == 83) { + demo_hier += .02; + } else if (c == 81) { + demo_hier -= .02; + if(demo_hier <= .0) demo_hier = .0; } - return (double)time.tv_sec + (double)time.tv_usec * .000001; + return 0; } -void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int frame_skip, char *prefix, float hier_thresh) +void *display_loop(void *ptr) { - //skip = frame_skip; + while(1){ + display_in_thread(0); + } +} + +void *detect_loop(void *ptr) +{ + while(1){ + detect_in_thread(0); + } +} + +void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int delay, char *prefix, int avg_frames, float hier, int w, int h, int frames, int fullscreen) +{ + //demo_frame = avg_frames; image **alphabet = load_alphabet(); - int delay = frame_skip; demo_names = names; demo_alphabet = alphabet; demo_classes = classes; demo_thresh = thresh; - demo_hier_thresh = hier_thresh; + demo_hier = hier; printf("Demo\n"); - net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + pthread_t detect_thread; + pthread_t fetch_thread; srand(2222222); + int i; + demo_total = size_network(net); + predictions = calloc(demo_frame, sizeof(float*)); + for (i = 0; i < demo_frame; ++i){ + predictions[i] = calloc(demo_total, sizeof(float)); + } + avg = calloc(demo_total, sizeof(float)); + if(filename){ printf("video file: %s\n", filename); - cap = cvCaptureFromFile(filename); + cap = open_video_stream(filename, 0, 0, 0, 0); }else{ - cap = cvCaptureFromCAM(cam_index); + cap = open_video_stream(0, cam_index, w, h, frames); } if(!cap) error("Couldn't connect to webcam.\n"); - layer l = net.layers[net.n-1]; - int j; - - avg = (float *) calloc(l.outputs, sizeof(float)); - for(j = 0; j < FRAMES; ++j) predictions[j] = (float *) calloc(l.outputs, sizeof(float)); - for(j = 0; j < FRAMES; ++j) images[j] = make_image(1,1,3); - - boxes = (box *)calloc(l.w*l.h*l.n, sizeof(box)); - probs = (float **)calloc(l.w*l.h*l.n, sizeof(float *)); - for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = (float *)calloc(l.classes, sizeof(float)); - - pthread_t fetch_thread; - pthread_t detect_thread; - - fetch_in_thread(0); - det = in; - det_s = in_s; - - fetch_in_thread(0); - detect_in_thread(0); - disp = det; - det = in; - det_s = in_s; - - for(j = 0; j < FRAMES/2; ++j){ - fetch_in_thread(0); - detect_in_thread(0); - disp = det; - det = in; - det_s = in_s; - } + buff[0] = get_image_from_stream(cap); + buff[1] = copy_image(buff[0]); + buff[2] = copy_image(buff[0]); + buff_letter[0] = letterbox_image(buff[0], net->w, net->h); + buff_letter[1] = letterbox_image(buff[0], net->w, net->h); + buff_letter[2] = letterbox_image(buff[0], net->w, net->h); int count = 0; if(!prefix){ - cvNamedWindow("Demo", CV_WINDOW_NORMAL); - cvMoveWindow("Demo", 0, 0); - cvResizeWindow("Demo", 1352, 1013); + make_window("Demo", 1352, 1013, fullscreen); } - double before = get_wall_time(); - - while(1){ - ++count; - if(1){ - if(pthread_create(&fetch_thread, 0, fetch_in_thread, 0)) error("Thread creation failed"); - if(pthread_create(&detect_thread, 0, detect_in_thread, 0)) error("Thread creation failed"); - - if(!prefix){ - show_image(disp, "Demo"); - int c = cvWaitKey(1); - if (c == 10){ - if(frame_skip == 0) frame_skip = 60; - else if(frame_skip == 4) frame_skip = 0; - else if(frame_skip == 60) frame_skip = 4; - else frame_skip = 0; - } - }else{ - char buff[256]; - sprintf(buff, "%s_%08d", prefix, count); - save_image(disp, buff); - } - - pthread_join(fetch_thread, 0); - pthread_join(detect_thread, 0); - - if(delay == 0){ - free_image(disp); - disp = det; - } - det = in; - det_s = in_s; - }else { - fetch_in_thread(0); - det = in; - det_s = in_s; - detect_in_thread(0); - if(delay == 0) { - free_image(disp); - disp = det; - } - show_image(disp, "Demo"); - cvWaitKey(1); - } - --delay; - if(delay < 0){ - delay = frame_skip; - - double after = get_wall_time(); - float curr = 1./(after - before); - fps = curr; - before = after; + demo_time = what_time_is_it_now(); + + while(!demo_done){ + buff_index = (buff_index + 1) %3; + if(pthread_create(&fetch_thread, 0, fetch_in_thread, 0)) error("Thread creation failed"); + if(pthread_create(&detect_thread, 0, detect_in_thread, 0)) error("Thread creation failed"); + if(!prefix){ + fps = 1./(what_time_is_it_now() - demo_time); + demo_time = what_time_is_it_now(); + display_in_thread(0); + }else{ + char name[256]; + sprintf(name, "%s_%08d", prefix, count); + save_image(buff[(buff_index + 1)%3], name); } + pthread_join(fetch_thread, 0); + pthread_join(detect_thread, 0); + ++count; } } + +/* + void demo_compare(char *cfg1, char *weight1, char *cfg2, char *weight2, float thresh, int cam_index, const char *filename, char **names, int classes, int delay, char *prefix, int avg_frames, float hier, int w, int h, int frames, int fullscreen) + { + demo_frame = avg_frames; + predictions = calloc(demo_frame, sizeof(float*)); + image **alphabet = load_alphabet(); + demo_names = names; + demo_alphabet = alphabet; + demo_classes = classes; + demo_thresh = thresh; + demo_hier = hier; + printf("Demo\n"); + net = load_network(cfg1, weight1, 0); + set_batch_network(net, 1); + pthread_t detect_thread; + pthread_t fetch_thread; + + srand(2222222); + + if(filename){ + printf("video file: %s\n", filename); + cap = cvCaptureFromFile(filename); + }else{ + cap = cvCaptureFromCAM(cam_index); + + if(w){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FRAME_WIDTH, w); + } + if(h){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FRAME_HEIGHT, h); + } + if(frames){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FPS, frames); + } + } + + if(!cap) error("Couldn't connect to webcam.\n"); + + layer l = net->layers[net->n-1]; + demo_detections = l.n*l.w*l.h; + int j; + + avg = (float *) calloc(l.outputs, sizeof(float)); + for(j = 0; j < demo_frame; ++j) predictions[j] = (float *) calloc(l.outputs, sizeof(float)); + + boxes = (box *)calloc(l.w*l.h*l.n, sizeof(box)); + probs = (float **)calloc(l.w*l.h*l.n, sizeof(float *)); + for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = (float *)calloc(l.classes+1, sizeof(float)); + + buff[0] = get_image_from_stream(cap); + buff[1] = copy_image(buff[0]); + buff[2] = copy_image(buff[0]); + buff_letter[0] = letterbox_image(buff[0], net->w, net->h); + buff_letter[1] = letterbox_image(buff[0], net->w, net->h); + buff_letter[2] = letterbox_image(buff[0], net->w, net->h); + ipl = cvCreateImage(cvSize(buff[0].w,buff[0].h), IPL_DEPTH_8U, buff[0].c); + + int count = 0; + if(!prefix){ + cvNamedWindow("Demo", CV_WINDOW_NORMAL); + if(fullscreen){ + cvSetWindowProperty("Demo", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN); + } else { + cvMoveWindow("Demo", 0, 0); + cvResizeWindow("Demo", 1352, 1013); + } + } + + demo_time = what_time_is_it_now(); + + while(!demo_done){ +buff_index = (buff_index + 1) %3; +if(pthread_create(&fetch_thread, 0, fetch_in_thread, 0)) error("Thread creation failed"); +if(pthread_create(&detect_thread, 0, detect_in_thread, 0)) error("Thread creation failed"); +if(!prefix){ + fps = 1./(what_time_is_it_now() - demo_time); + demo_time = what_time_is_it_now(); + display_in_thread(0); +}else{ + char name[256]; + sprintf(name, "%s_%08d", prefix, count); + save_image(buff[(buff_index + 1)%3], name); +} +pthread_join(fetch_thread, 0); +pthread_join(detect_thread, 0); +++count; +} +} +*/ #else -void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int frame_skip, char *prefix, float hier_thresh) +void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int delay, char *prefix, int avg, float hier, int w, int h, int frames, int fullscreen) { fprintf(stderr, "Demo needs OpenCV for webcam images.\n"); } diff --git a/image.darknet/src/demo.h b/image.darknet/src/demo.h index c3d6a61..86e4654 100644 --- a/image.darknet/src/demo.h +++ b/image.darknet/src/demo.h @@ -1,7 +1,6 @@ -#ifndef DEMO -#define DEMO +#ifndef DEMO_H +#define DEMO_H #include "image.h" -void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int frame_skip, char *prefix, float hier_thresh); #endif diff --git a/image.darknet/src/detection_layer.c b/image.darknet/src/detection_layer.c index cd98b4b..d0e0194 100644 --- a/image.darknet/src/detection_layer.c +++ b/image.darknet/src/detection_layer.c @@ -5,6 +5,7 @@ #include "box.h" #include "cuda.h" #include "utils.h" + #include #include #include @@ -46,11 +47,11 @@ detection_layer make_detection_layer(int batch, int inputs, int n, int side, int return l; } -void forward_detection_layer(const detection_layer l, network_state state) +void forward_detection_layer(const detection_layer l, network net) { int locations = l.side*l.side; int i,j; - memcpy(l.output, state.input, l.outputs*l.batch*sizeof(float)); + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); //if(l.reorg) reorg(l.output, l.w*l.h, size*l.n, l.batch, 1); int b; if (l.softmax){ @@ -58,12 +59,12 @@ void forward_detection_layer(const detection_layer l, network_state state) int index = b*l.inputs; for (i = 0; i < locations; ++i) { int offset = i*l.classes; - softmax(l.output + index + offset, l.classes, 1, + softmax(l.output + index + offset, l.classes, 1, 1, l.output + index + offset); } } } - if(state.train){ + if(net.train){ float avg_iou = 0; float avg_cat = 0; float avg_allcat = 0; @@ -77,7 +78,7 @@ void forward_detection_layer(const detection_layer l, network_state state) int index = b*l.inputs; for (i = 0; i < locations; ++i) { int truth_index = (b*locations + i)*(1+l.coords+l.classes); - int is_obj = state.truth[truth_index]; + int is_obj = net.truth[truth_index]; for (j = 0; j < l.n; ++j) { int p_index = index + locations*l.classes + i*l.n + j; l.delta[p_index] = l.noobject_scale*(0 - l.output[p_index]); @@ -95,19 +96,19 @@ void forward_detection_layer(const detection_layer l, network_state state) int class_index = index + i*l.classes; for(j = 0; j < l.classes; ++j) { - l.delta[class_index+j] = l.class_scale * (state.truth[truth_index+1+j] - l.output[class_index+j]); - *(l.cost) += l.class_scale * pow(state.truth[truth_index+1+j] - l.output[class_index+j], 2); - if(state.truth[truth_index + 1 + j]) avg_cat += l.output[class_index+j]; + l.delta[class_index+j] = l.class_scale * (net.truth[truth_index+1+j] - l.output[class_index+j]); + *(l.cost) += l.class_scale * pow(net.truth[truth_index+1+j] - l.output[class_index+j], 2); + if(net.truth[truth_index + 1 + j]) avg_cat += l.output[class_index+j]; avg_allcat += l.output[class_index+j]; } - box truth = float_to_box(state.truth + truth_index + 1 + l.classes); + box truth = float_to_box(net.truth + truth_index + 1 + l.classes, 1); truth.x /= l.side; truth.y /= l.side; for(j = 0; j < l.n; ++j){ int box_index = index + locations*(l.classes + l.n) + (i*l.n + j) * l.coords; - box out = float_to_box(l.output + box_index); + box out = float_to_box(l.output + box_index, 1); out.x /= l.side; out.y /= l.side; @@ -139,14 +140,14 @@ void forward_detection_layer(const detection_layer l, network_state state) best_index = 0; } } - if(l.random && *(state.net.seen) < 64000){ + if(l.random && *(net.seen) < 64000){ best_index = rand()%l.n; } int box_index = index + locations*(l.classes + l.n) + (i*l.n + best_index) * l.coords; int tbox_index = truth_index + 1 + l.classes; - box out = float_to_box(l.output + box_index); + box out = float_to_box(l.output + box_index, 1); out.x /= l.side; out.y /= l.side; if (l.sqrt) { @@ -166,13 +167,13 @@ void forward_detection_layer(const detection_layer l, network_state state) l.delta[p_index] = l.object_scale * (iou - l.output[p_index]); } - l.delta[box_index+0] = l.coord_scale*(state.truth[tbox_index + 0] - l.output[box_index + 0]); - l.delta[box_index+1] = l.coord_scale*(state.truth[tbox_index + 1] - l.output[box_index + 1]); - l.delta[box_index+2] = l.coord_scale*(state.truth[tbox_index + 2] - l.output[box_index + 2]); - l.delta[box_index+3] = l.coord_scale*(state.truth[tbox_index + 3] - l.output[box_index + 3]); + l.delta[box_index+0] = l.coord_scale*(net.truth[tbox_index + 0] - l.output[box_index + 0]); + l.delta[box_index+1] = l.coord_scale*(net.truth[tbox_index + 1] - l.output[box_index + 1]); + l.delta[box_index+2] = l.coord_scale*(net.truth[tbox_index + 2] - l.output[box_index + 2]); + l.delta[box_index+3] = l.coord_scale*(net.truth[tbox_index + 3] - l.output[box_index + 3]); if(l.sqrt){ - l.delta[box_index+2] = l.coord_scale*(sqrt(state.truth[tbox_index + 2]) - l.output[box_index + 2]); - l.delta[box_index+3] = l.coord_scale*(sqrt(state.truth[tbox_index + 3]) - l.output[box_index + 3]); + l.delta[box_index+2] = l.coord_scale*(sqrt(net.truth[tbox_index + 2]) - l.output[box_index + 2]); + l.delta[box_index+3] = l.coord_scale*(sqrt(net.truth[tbox_index + 3]) - l.output[box_index + 3]); } *(l.cost) += pow(1-iou, 2); @@ -216,12 +217,12 @@ void forward_detection_layer(const detection_layer l, network_state state) } } -void backward_detection_layer(const detection_layer l, network_state state) +void backward_detection_layer(const detection_layer l, network net) { - axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, state.delta, 1); + axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, net.delta, 1); } -void get_detection_boxes(layer l, int w, int h, float thresh, float **probs, box *boxes, int only_objectness) +void get_detection_detections(layer l, int w, int h, float thresh, detection *dets) { int i,j,n; float *predictions = l.output; @@ -234,17 +235,17 @@ void get_detection_boxes(layer l, int w, int h, float thresh, float **probs, box int p_index = l.side*l.side*l.classes + i*l.n + n; float scale = predictions[p_index]; int box_index = l.side*l.side*(l.classes + l.n) + (i*l.n + n)*4; - boxes[index].x = (predictions[box_index + 0] + col) / l.side * w; - boxes[index].y = (predictions[box_index + 1] + row) / l.side * h; - boxes[index].w = pow(predictions[box_index + 2], (l.sqrt?2:1)) * w; - boxes[index].h = pow(predictions[box_index + 3], (l.sqrt?2:1)) * h; + box b; + b.x = (predictions[box_index + 0] + col) / l.side * w; + b.y = (predictions[box_index + 1] + row) / l.side * h; + b.w = pow(predictions[box_index + 2], (l.sqrt?2:1)) * w; + b.h = pow(predictions[box_index + 3], (l.sqrt?2:1)) * h; + dets[index].bbox = b; + dets[index].objectness = scale; for(j = 0; j < l.classes; ++j){ int class_index = i*l.classes; float prob = scale*predictions[class_index+j]; - probs[index][j] = (prob > thresh) ? prob : 0; - } - if(only_objectness){ - probs[index][0] = scale; + dets[index].prob[j] = (prob > thresh) ? prob : 0; } } } @@ -252,36 +253,23 @@ void get_detection_boxes(layer l, int w, int h, float thresh, float **probs, box #ifdef GPU -void forward_detection_layer_gpu(const detection_layer l, network_state state) +void forward_detection_layer_gpu(const detection_layer l, network net) { - if(!state.train){ - copy_ongpu(l.batch*l.inputs, state.input, 1, l.output_gpu, 1); + if(!net.train){ + copy_gpu(l.batch*l.inputs, net.input_gpu, 1, l.output_gpu, 1); return; } - float *in_cpu = calloc(l.batch*l.inputs, sizeof(float)); - float *truth_cpu = 0; - if(state.truth){ - int num_truth = l.batch*l.side*l.side*(1+l.coords+l.classes); - truth_cpu = calloc(num_truth, sizeof(float)); - cuda_pull_array(state.truth, truth_cpu, num_truth); - } - cuda_pull_array(state.input, in_cpu, l.batch*l.inputs); - network_state cpu_state = state; - cpu_state.train = state.train; - cpu_state.truth = truth_cpu; - cpu_state.input = in_cpu; - forward_detection_layer(l, cpu_state); + cuda_pull_array(net.input_gpu, net.input, l.batch*l.inputs); + forward_detection_layer(l, net); cuda_push_array(l.output_gpu, l.output, l.batch*l.outputs); cuda_push_array(l.delta_gpu, l.delta, l.batch*l.inputs); - free(cpu_state.input); - if(cpu_state.truth) free(cpu_state.truth); } -void backward_detection_layer_gpu(detection_layer l, network_state state) +void backward_detection_layer_gpu(detection_layer l, network net) { - axpy_ongpu(l.batch*l.inputs, 1, l.delta_gpu, 1, state.delta, 1); - //copy_ongpu(l.batch*l.inputs, l.delta_gpu, 1, state.delta, 1); + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); + //copy_gpu(l.batch*l.inputs, l.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/src/detection_layer.h b/image.darknet/src/detection_layer.h index e847a09..1c81853 100644 --- a/image.darknet/src/detection_layer.h +++ b/image.darknet/src/detection_layer.h @@ -7,13 +7,12 @@ typedef layer detection_layer; detection_layer make_detection_layer(int batch, int inputs, int n, int size, int classes, int coords, int rescore); -void forward_detection_layer(const detection_layer l, network_state state); -void backward_detection_layer(const detection_layer l, network_state state); -void get_detection_boxes(layer l, int w, int h, float thresh, float **probs, box *boxes, int only_objectness); +void forward_detection_layer(const detection_layer l, network net); +void backward_detection_layer(const detection_layer l, network net); #ifdef GPU -void forward_detection_layer_gpu(const detection_layer l, network_state state); -void backward_detection_layer_gpu(detection_layer l, network_state state); +void forward_detection_layer_gpu(const detection_layer l, network net); +void backward_detection_layer_gpu(detection_layer l, network net); #endif #endif diff --git a/image.darknet/src/detector.c b/image.darknet/src/detector.c deleted file mode 100644 index 1416c05..0000000 --- a/image.darknet/src/detector.c +++ /dev/null @@ -1,552 +0,0 @@ -#include "network.h" -#include "region_layer.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "box.h" -#include "demo.h" -#include "option_list.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif -static int coco_ids[] = {1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,27,28,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,70,72,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,90}; - -void train_detector(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear) -{ - list *options = read_data_cfg(datacfg); - char *train_images = option_find_str(options, "train", "data/train.list"); - char *backup_directory = option_find_str(options, "backup", "/backup/"); - - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - float avg_loss = -1; - network *nets = calloc(ngpus, sizeof(network)); - - srand(time(0)); - int seed = rand(); - int i; - for(i = 0; i < ngpus; ++i){ - srand(seed); -#ifdef GPU - cuda_set_device(gpus[i]); -#endif - nets[i] = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&nets[i], weightfile); - } - if(clear) *nets[i].seen = 0; - nets[i].learning_rate *= ngpus; - } - srand(time(0)); - network net = nets[0]; - - int imgs = net.batch * net.subdivisions * ngpus; - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - data train, buffer; - - layer l = net.layers[net.n - 1]; - - int classes = l.classes; - float jitter = l.jitter; - - list *plist = get_paths(train_images); - //int N = plist->size; - char **paths = (char **)list_to_array(plist); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.paths = paths; - args.n = imgs; - args.m = plist->size; - args.classes = classes; - args.jitter = jitter; - args.num_boxes = l.max_boxes; - args.d = &buffer; - args.type = DETECTION_DATA; - args.threads = 8; - - args.angle = net.angle; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; - - pthread_t load_thread = load_data(args); - clock_t time; - int count = 0; - //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ - if(l.random && count++%10 == 0){ - printf("Resizing\n"); - int dim = (rand() % 10 + 10) * 32; - if (get_current_batch(net)+200 > net.max_batches) dim = 608; - //int dim = (rand() % 4 + 16) * 32; - printf("%d\n", dim); - args.w = dim; - args.h = dim; - - pthread_join(load_thread, 0); - train = buffer; - free_data(train); - load_thread = load_data(args); - - for(i = 0; i < ngpus; ++i){ - resize_network(nets + i, dim, dim); - } - net = nets[0]; - } - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data(args); - - /* - int k; - for(k = 0; k < l.max_boxes; ++k){ - box b = float_to_box(train.y.vals[10] + 1 + k*5); - if(!b.x) break; - printf("loaded: %f %f %f %f\n", b.x, b.y, b.w, b.h); - } - image im = float_to_image(448, 448, 3, train.X.vals[10]); - int k; - for(k = 0; k < l.max_boxes; ++k){ - box b = float_to_box(train.y.vals[10] + 1 + k*5); - printf("%d %d %d %d\n", truth.x, truth.y, truth.w, truth.h); - draw_bbox(im, b, 8, 1,0,0); - } - save_image(im, "truth11"); - */ - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - - time=clock(); - float loss = 0; -#ifdef GPU - if(ngpus == 1){ - loss = train_network(net, train); - } else { - loss = train_networks(nets, ngpus, train, 4); - } -#else - loss = train_network(net, train); -#endif - if (avg_loss < 0) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - - i = get_current_batch(net); - printf("%d: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), loss, avg_loss, get_current_rate(net), sec(clock()-time), i*imgs); - if(i%1000==0 || (i < 1000 && i%100 == 0)){ -#ifdef GPU - if(ngpus != 1) sync_nets(nets, ngpus, 0); -#endif - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); - save_weights(net, buff); - } - free_data(train); - } -#ifdef GPU - if(ngpus != 1) sync_nets(nets, ngpus, 0); -#endif - char buff[256]; - sprintf(buff, "%s/%s_final.weights", backup_directory, base); - save_weights(net, buff); -} - - -static int get_coco_image_id(char *filename) -{ - char *p = strrchr(filename, '_'); - return atoi(p+1); -} - -static void print_cocos(FILE *fp, char *image_path, box *boxes, float **probs, int num_boxes, int classes, int w, int h) -{ - int i, j; - int image_id = get_coco_image_id(image_path); - for(i = 0; i < num_boxes; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; - - if (xmin < 0) xmin = 0; - if (ymin < 0) ymin = 0; - if (xmax > w) xmax = w; - if (ymax > h) ymax = h; - - float bx = xmin; - float by = ymin; - float bw = xmax - xmin; - float bh = ymax - ymin; - - for(j = 0; j < classes; ++j){ - if (probs[i][j]) fprintf(fp, "{\"image_id\":%d, \"category_id\":%d, \"bbox\":[%f, %f, %f, %f], \"score\":%f},\n", image_id, coco_ids[j], bx, by, bw, bh, probs[i][j]); - } - } -} - -void print_detector_detections(FILE **fps, char *id, box *boxes, float **probs, int total, int classes, int w, int h) -{ - int i, j; - for(i = 0; i < total; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; - - if (xmin < 0) xmin = 0; - if (ymin < 0) ymin = 0; - if (xmax > w) xmax = w; - if (ymax > h) ymax = h; - - for(j = 0; j < classes; ++j){ - if (probs[i][j]) fprintf(fps[j], "%s %f %f %f %f %f\n", id, probs[i][j], - xmin, ymin, xmax, ymax); - } - } -} - -void print_imagenet_detections(FILE *fp, int id, box *boxes, float **probs, int total, int classes, int w, int h) -{ - int i, j; - for(i = 0; i < total; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; - - if (xmin < 0) xmin = 0; - if (ymin < 0) ymin = 0; - if (xmax > w) xmax = w; - if (ymax > h) ymax = h; - - for(j = 0; j < classes; ++j){ - int class = j; - if (probs[i][class]) fprintf(fp, "%d %d %f %f %f %f %f\n", id, j+1, probs[i][class], - xmin, ymin, xmax, ymax); - } - } -} - -void validate_detector(char *datacfg, char *cfgfile, char *weightfile, char *outfile) -{ - int j; - list *options = read_data_cfg(datacfg); - char *valid_images = option_find_str(options, "valid", "data/train.list"); - char *name_list = option_find_str(options, "names", "data/names.list"); - char *prefix = option_find_str(options, "results", "results"); - char **names = get_labels(name_list); - char *mapf = option_find_str(options, "map", 0); - int *map = 0; - if (mapf) map = read_map(mapf); - - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - srand(time(0)); - - list *plist = get_paths(valid_images); - char **paths = (char **)list_to_array(plist); - - layer l = net.layers[net.n-1]; - int classes = l.classes; - - char buff[1024]; - char *type = option_find_str(options, "eval", "voc"); - FILE *fp = 0; - FILE **fps = 0; - int coco = 0; - int imagenet = 0; - if(0==strcmp(type, "coco")){ - if(!outfile) outfile = "coco_results"; - snprintf(buff, 1024, "%s/%s.json", prefix, outfile); - fp = fopen(buff, "w"); - fprintf(fp, "[\n"); - coco = 1; - } else if(0==strcmp(type, "imagenet")){ - if(!outfile) outfile = "imagenet-detection"; - snprintf(buff, 1024, "%s/%s.txt", prefix, outfile); - fp = fopen(buff, "w"); - imagenet = 1; - classes = 200; - } else { - if(!outfile) outfile = "comp4_det_test_"; - fps = calloc(classes, sizeof(FILE *)); - for(j = 0; j < classes; ++j){ - snprintf(buff, 1024, "%s/%s%s.txt", prefix, outfile, names[j]); - fps[j] = fopen(buff, "w"); - } - } - - - box *boxes = calloc(l.w*l.h*l.n, sizeof(box)); - float **probs = calloc(l.w*l.h*l.n, sizeof(float *)); - for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); - - int m = plist->size; - int i=0; - int t; - - float thresh = .005; - float nms = .45; - - int nthreads = 4; - image *val = calloc(nthreads, sizeof(image)); - image *val_resized = calloc(nthreads, sizeof(image)); - image *buf = calloc(nthreads, sizeof(image)); - image *buf_resized = calloc(nthreads, sizeof(image)); - pthread_t *thr = calloc(nthreads, sizeof(pthread_t)); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.type = IMAGE_DATA; - - for(t = 0; t < nthreads; ++t){ - args.path = paths[i+t]; - args.im = &buf[t]; - args.resized = &buf_resized[t]; - thr[t] = load_data_in_thread(args); - } - time_t start = time(0); - for(i = nthreads; i < m+nthreads; i += nthreads){ - fprintf(stderr, "%d\n", i); - for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ - pthread_join(thr[t], 0); - val[t] = buf[t]; - val_resized[t] = buf_resized[t]; - } - for(t = 0; t < nthreads && i+t < m; ++t){ - args.path = paths[i+t]; - args.im = &buf[t]; - args.resized = &buf_resized[t]; - thr[t] = load_data_in_thread(args); - } - for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ - char *path = paths[i+t-nthreads]; - char *id = basecfg(path); - float *X = val_resized[t].data; - network_predict(net, X); - int w = val[t].w; - int h = val[t].h; - get_region_boxes(l, w, h, thresh, probs, boxes, 0, map, .5); - if (nms) do_nms_sort(boxes, probs, l.w*l.h*l.n, classes, nms); - if (coco){ - print_cocos(fp, path, boxes, probs, l.w*l.h*l.n, classes, w, h); - } else if (imagenet){ - print_imagenet_detections(fp, i+t-nthreads+1, boxes, probs, l.w*l.h*l.n, classes, w, h); - } else { - print_detector_detections(fps, id, boxes, probs, l.w*l.h*l.n, classes, w, h); - } - free(id); - free_image(val[t]); - free_image(val_resized[t]); - } - } - for(j = 0; j < classes; ++j){ - if(fps) fclose(fps[j]); - } - if(coco){ - fseek(fp, -2, SEEK_CUR); - fprintf(fp, "\n]\n"); - fclose(fp); - } - fprintf(stderr, "Total Detection Time: %f Seconds\n", (double)(time(0) - start)); -} - -void validate_detector_recall(char *cfgfile, char *weightfile) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - srand(time(0)); - - list *plist = get_paths("data/voc.2007.test"); - char **paths = (char **)list_to_array(plist); - - layer l = net.layers[net.n-1]; - int classes = l.classes; - - int j, k; - box *boxes = calloc(l.w*l.h*l.n, sizeof(box)); - float **probs = calloc(l.w*l.h*l.n, sizeof(float *)); - for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); - - int m = plist->size; - int i=0; - - float thresh = .001; - float iou_thresh = .5; - float nms = .4; - - int total = 0; - int correct = 0; - int proposals = 0; - float avg_iou = 0; - - for(i = 0; i < m; ++i){ - char *path = paths[i]; - image orig = load_image_color(path, 0, 0); - image sized = resize_image(orig, net.w, net.h); - char *id = basecfg(path); - network_predict(net, sized.data); - get_region_boxes(l, 1, 1, thresh, probs, boxes, 1, 0, .5); - if (nms) do_nms(boxes, probs, l.w*l.h*l.n, 1, nms); - - char labelpath[4096]; - find_replace(path, "images", "labels", labelpath); - find_replace(labelpath, "JPEGImages", "labels", labelpath); - find_replace(labelpath, ".jpg", ".txt", labelpath); - find_replace(labelpath, ".JPEG", ".txt", labelpath); - - int num_labels = 0; - box_label *truth = read_boxes(labelpath, &num_labels); - for(k = 0; k < l.w*l.h*l.n; ++k){ - if(probs[k][0] > thresh){ - ++proposals; - } - } - for (j = 0; j < num_labels; ++j) { - ++total; - box t = {truth[j].x, truth[j].y, truth[j].w, truth[j].h}; - float best_iou = 0; - for(k = 0; k < l.w*l.h*l.n; ++k){ - float iou = box_iou(boxes[k], t); - if(probs[k][0] > thresh && iou > best_iou){ - best_iou = iou; - } - } - avg_iou += best_iou; - if(best_iou > iou_thresh){ - ++correct; - } - } - - fprintf(stderr, "%5d %5d %5d\tRPs/Img: %.2f\tIOU: %.2f%%\tRecall:%.2f%%\n", i, correct, total, (float)proposals/(i+1), avg_iou*100/total, 100.*correct/total); - free(id); - free_image(orig); - free_image(sized); - } -} - -void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh) -{ - list *options = read_data_cfg(datacfg); - char *name_list = option_find_str(options, "names", "data/names.list"); - char **names = get_labels(name_list); - - image **alphabet = load_alphabet(); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - clock_t time; - char buff[256]; - char *input = buff; - int j; - float nms=.4; - 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, "\n"); - } - image im = load_image_color(input,0,0); - image sized = resize_image(im, net.w, net.h); - layer l = net.layers[net.n-1]; - - box *boxes = calloc(l.w*l.h*l.n, sizeof(box)); - float **probs = calloc(l.w*l.h*l.n, sizeof(float *)); - for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = calloc(l.classes + 1, sizeof(float *)); - - float *X = sized.data; - time=clock(); - network_predict(net, X); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - get_region_boxes(l, 1, 1, thresh, probs, boxes, 0, 0, hier_thresh); - if (l.softmax_tree && nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms); - else if (nms) do_nms_sort(boxes, probs, l.w*l.h*l.n, l.classes, nms); - draw_detections(im, l.w*l.h*l.n, thresh, boxes, probs, names, alphabet, l.classes); - save_image(im, "predictions"); - show_image(im, "predictions"); - - free_image(im); - free_image(sized); - free(boxes); - free_ptrs((void **)probs, l.w*l.h*l.n); -#ifdef OPENCV - cvWaitKey(0); - cvDestroyAllWindows(); -#endif - if (filename) break; - } -} - -void run_detector(int argc, char **argv) -{ - char *prefix = find_char_arg(argc, argv, "-prefix", 0); - float thresh = find_float_arg(argc, argv, "-thresh", .24); - float hier_thresh = find_float_arg(argc, argv, "-hier", .5); - int cam_index = find_int_arg(argc, argv, "-c", 0); - int frame_skip = find_int_arg(argc, argv, "-s", 0); - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); - char *outfile = find_char_arg(argc, argv, "-out", 0); - int *gpus = 0; - int gpu = 0; - int ngpus = 0; - if(gpu_list){ - printf("%s\n", gpu_list); - int len = strlen(gpu_list); - ngpus = 1; - int i; - for(i = 0; i < len; ++i){ - if (gpu_list[i] == ',') ++ngpus; - } - gpus = calloc(ngpus, sizeof(int)); - for(i = 0; i < ngpus; ++i){ - gpus[i] = atoi(gpu_list); - gpu_list = strchr(gpu_list, ',')+1; - } - } else { - gpu = gpu_index; - gpus = &gpu; - ngpus = 1; - } - - int clear = find_arg(argc, argv, "-clear"); - - char *datacfg = argv[3]; - char *cfg = argv[4]; - char *weights = (argc > 5) ? argv[5] : 0; - char *filename = (argc > 6) ? argv[6]: 0; - if(0==strcmp(argv[2], "test")) test_detector(datacfg, cfg, weights, filename, thresh, hier_thresh); - else if(0==strcmp(argv[2], "train")) train_detector(datacfg, cfg, weights, gpus, ngpus, clear); - else if(0==strcmp(argv[2], "valid")) validate_detector(datacfg, cfg, weights, outfile); - else if(0==strcmp(argv[2], "recall")) validate_detector_recall(cfg, weights); - else if(0==strcmp(argv[2], "demo")) { - list *options = read_data_cfg(datacfg); - int classes = option_find_int(options, "classes", 20); - char *name_list = option_find_str(options, "names", "data/names.list"); - char **names = get_labels(name_list); - demo(cfg, weights, thresh, cam_index, filename, names, classes, frame_skip, prefix, hier_thresh); - } -} diff --git a/image.darknet/src/dice.c b/image.darknet/src/dice.c deleted file mode 100644 index 2286459..0000000 --- a/image.darknet/src/dice.c +++ /dev/null @@ -1,118 +0,0 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" - -char *dice_labels[] = {"face1","face2","face3","face4","face5","face6"}; - -void train_dice(char *cfgfile, char *weightfile) -{ - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - char *backup_directory = "/home/pjreddie/backup/"; - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = 1024; - int i = *net.seen/imgs; - char **labels = dice_labels; - list *plist = get_paths("data/dice/dice.train.list"); - char **paths = (char **)list_to_array(plist); - printf("%d\n", plist->size); - clock_t time; - while(1){ - ++i; - time=clock(); - data train = load_data_old(paths, imgs, plist->size, labels, 6, net.w, net.h); - printf("Loaded: %lf seconds\n", sec(clock()-time)); - - time=clock(); - float loss = train_network(net, train); - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), *net.seen); - free_data(train); - if((i % 100) == 0) net.learning_rate *= .1; - if(i%100==0){ - char buff[256]; - sprintf(buff, "%s/%s_%d.weights",backup_directory,base, i); - save_weights(net, buff); - } - } -} - -void validate_dice(char *filename, char *weightfile) -{ - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - srand(time(0)); - - char **labels = dice_labels; - list *plist = get_paths("data/dice/dice.val.list"); - - char **paths = (char **)list_to_array(plist); - int m = plist->size; - free_list(plist); - - data val = load_data_old(paths, m, 0, labels, 6, net.w, net.h); - float *acc = network_accuracies(net, val, 2); - printf("Validation Accuracy: %f, %d images\n", acc[0], m); - free_data(val); -} - -void test_dice(char *cfgfile, char *weightfile, char *filename) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - int i = 0; - char **names = dice_labels; - char buff[256]; - char *input = buff; - int indexes[6]; - 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, "\n"); - } - image im = load_image_color(input, net.w, net.h); - float *X = im.data; - float *predictions = network_predict(net, X); - top_predictions(net, 6, indexes); - for(i = 0; i < 6; ++i){ - int index = indexes[i]; - printf("%s: %f\n", names[index], predictions[index]); - } - free_image(im); - if (filename) break; - } -} - -void run_dice(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *filename = (argc > 5) ? argv[5]: 0; - if(0==strcmp(argv[2], "test")) test_dice(cfg, weights, filename); - else if(0==strcmp(argv[2], "train")) train_dice(cfg, weights); - else if(0==strcmp(argv[2], "valid")) validate_dice(cfg, weights); -} - diff --git a/image.darknet/src/dropout_layer.c b/image.darknet/src/dropout_layer.c index b1381e6..780554f 100644 --- a/image.darknet/src/dropout_layer.c +++ b/image.darknet/src/dropout_layer.c @@ -35,26 +35,26 @@ void resize_dropout_layer(dropout_layer *l, int inputs) #endif } -void forward_dropout_layer(dropout_layer l, network_state state) +void forward_dropout_layer(dropout_layer l, network net) { int i; - if (!state.train) return; + if (!net.train) return; for(i = 0; i < l.batch * l.inputs; ++i){ float r = rand_uniform(0, 1); l.rand[i] = r; - if(r < l.probability) state.input[i] = 0; - else state.input[i] *= l.scale; + if(r < l.probability) net.input[i] = 0; + else net.input[i] *= l.scale; } } -void backward_dropout_layer(dropout_layer l, network_state state) +void backward_dropout_layer(dropout_layer l, network net) { int i; - if(!state.delta) return; + if(!net.delta) return; for(i = 0; i < l.batch * l.inputs; ++i){ float r = l.rand[i]; - if(r < l.probability) state.delta[i] = 0; - else state.delta[i] *= l.scale; + if(r < l.probability) net.delta[i] = 0; + else net.delta[i] *= l.scale; } } diff --git a/image.darknet/src/dropout_layer.h b/image.darknet/src/dropout_layer.h index 691cfc5..01f94d4 100644 --- a/image.darknet/src/dropout_layer.h +++ b/image.darknet/src/dropout_layer.h @@ -8,13 +8,13 @@ typedef layer dropout_layer; dropout_layer make_dropout_layer(int batch, int inputs, float probability); -void forward_dropout_layer(dropout_layer l, network_state state); -void backward_dropout_layer(dropout_layer l, network_state state); +void forward_dropout_layer(dropout_layer l, network net); +void backward_dropout_layer(dropout_layer l, network net); void resize_dropout_layer(dropout_layer *l, int inputs); #ifdef GPU -void forward_dropout_layer_gpu(dropout_layer l, network_state state); -void backward_dropout_layer_gpu(dropout_layer l, network_state state); +void forward_dropout_layer_gpu(dropout_layer l, network net); +void backward_dropout_layer_gpu(dropout_layer l, network net); #endif #endif diff --git a/image.darknet/src/dropout_layer_kernels.cu b/image.darknet/src/dropout_layer_kernels.cu index 7e51bd5..bd12b67 100644 --- a/image.darknet/src/dropout_layer_kernels.cu +++ b/image.darknet/src/dropout_layer_kernels.cu @@ -14,9 +14,9 @@ __global__ void yoloswag420blazeit360noscope(float *input, int size, float *rand if(id < size) input[id] = (rand[id] < prob) ? 0 : input[id]*scale; } -void forward_dropout_layer_gpu(dropout_layer layer, network_state state) +void forward_dropout_layer_gpu(dropout_layer layer, network net) { - if (!state.train) return; + if (!net.train) return; int size = layer.inputs*layer.batch; cuda_random(layer.rand_gpu, size); /* @@ -27,15 +27,15 @@ void forward_dropout_layer_gpu(dropout_layer layer, network_state state) cuda_push_array(layer.rand_gpu, layer.rand, size); */ - yoloswag420blazeit360noscope<<>>(state.input, size, layer.rand_gpu, layer.probability, layer.scale); + yoloswag420blazeit360noscope<<>>(net.input_gpu, size, layer.rand_gpu, layer.probability, layer.scale); check_error(cudaPeekAtLastError()); } -void backward_dropout_layer_gpu(dropout_layer layer, network_state state) +void backward_dropout_layer_gpu(dropout_layer layer, network net) { - if(!state.delta) return; + if(!net.delta_gpu) return; int size = layer.inputs*layer.batch; - yoloswag420blazeit360noscope<<>>(state.delta, size, layer.rand_gpu, layer.probability, layer.scale); + yoloswag420blazeit360noscope<<>>(net.delta_gpu, size, layer.rand_gpu, layer.probability, layer.scale); check_error(cudaPeekAtLastError()); } diff --git a/image.darknet/src/gemm.c b/image.darknet/src/gemm.c index 3003be0..648027f 100644 --- a/image.darknet/src/gemm.c +++ b/image.darknet/src/gemm.c @@ -77,6 +77,7 @@ void gemm_nn(int M, int N, int K, float ALPHA, float *C, int ldc) { int i,j,k; + #pragma omp parallel for for(i = 0; i < M; ++i){ for(k = 0; k < K; ++k){ register float A_PART = ALPHA*A[i*lda+k]; @@ -93,6 +94,7 @@ void gemm_nt(int M, int N, int K, float ALPHA, float *C, int ldc) { int i,j,k; + #pragma omp parallel for for(i = 0; i < M; ++i){ for(j = 0; j < N; ++j){ register float sum = 0; @@ -110,6 +112,7 @@ void gemm_tn(int M, int N, int K, float ALPHA, float *C, int ldc) { int i,j,k; + #pragma omp parallel for for(i = 0; i < M; ++i){ for(k = 0; k < K; ++k){ register float A_PART = ALPHA*A[k*lda+i]; @@ -126,6 +129,7 @@ void gemm_tt(int M, int N, int K, float ALPHA, float *C, int ldc) { int i,j,k; + #pragma omp parallel for for(i = 0; i < M; ++i){ for(j = 0; j < N; ++j){ register float sum = 0; @@ -165,7 +169,7 @@ void gemm_cpu(int TA, int TB, int M, int N, int K, float ALPHA, #include -void gemm_ongpu(int TA, int TB, int M, int N, int K, float ALPHA, +void gemm_gpu(int TA, int TB, int M, int N, int K, float ALPHA, float *A_gpu, int lda, float *B_gpu, int ldb, float BETA, @@ -177,24 +181,6 @@ void gemm_ongpu(int TA, int TB, int M, int N, int K, float ALPHA, check_error(status); } -void gemm_gpu(int TA, int TB, int M, int N, int K, float ALPHA, - float *A, int lda, - float *B, int ldb, - float BETA, - float *C, int ldc) -{ - float *A_gpu = cuda_make_array(A, (TA ? lda*K:lda*M)); - float *B_gpu = cuda_make_array(B, (TB ? ldb*N : ldb*K)); - float *C_gpu = cuda_make_array(C, ldc*M); - - gemm_ongpu(TA, TB, M, N, K, ALPHA, A_gpu, lda, B_gpu, ldb, BETA, C_gpu, ldc); - - cuda_pull_array(C_gpu, C, ldc*M); - cuda_free(A_gpu); - cuda_free(B_gpu); - cuda_free(C_gpu); -} - #include #include #include @@ -224,7 +210,7 @@ void time_gpu_random_matrix(int TA, int TB, int m, int k, int n) free(c); } -void time_ongpu(int TA, int TB, int m, int k, int n) +void time_gpu(int TA, int TB, int m, int k, int n) { int iter = 10; float *a = random_matrix(m,k); @@ -242,7 +228,7 @@ void time_ongpu(int TA, int TB, int m, int k, int n) int i; clock_t start = clock(), end; for(i = 0; i= m.n){ - m.n *= 2; - m.data = realloc(m.data, m.n*sizeof(char*)); - } - m.data[count] = line; - ++count; - } - printf("%d\n", count); - m.n = count; - m.data = realloc(m.data, count*sizeof(char*)); - return m; -} - -void string_to_board(char *s, float *board) -{ - int i, j; - //memset(board, 0, 1*19*19*sizeof(float)); - int count = 0; - for(i = 0; i < 91; ++i){ - char c = s[i]; - for(j = 0; j < 4; ++j){ - int me = (c >> (2*j)) & 1; - int you = (c >> (2*j + 1)) & 1; - if (me) board[count] = 1; - else if (you) board[count] = -1; - else board[count] = 0; - ++count; - if(count >= 19*19) break; - } - } -} - -void board_to_string(char *s, float *board) -{ - int i, j; - memset(s, 0, (19*19/4+1)*sizeof(char)); - int count = 0; - for(i = 0; i < 91; ++i){ - for(j = 0; j < 4; ++j){ - int me = (board[count] == 1); - int you = (board[count] == -1); - if (me) s[i] = s[i] | (1<<(2*j)); - if (you) s[i] = s[i] | (1<<(2*j + 1)); - ++count; - if(count >= 19*19) break; - } - } -} - -void random_go_moves(moves m, float *boards, float *labels, int n) -{ - int i; - memset(labels, 0, 19*19*n*sizeof(float)); - for(i = 0; i < n; ++i){ - char *b = m.data[rand()%m.n]; - int row = b[0]; - int col = b[1]; - labels[col + 19*(row + i*19)] = 1; - string_to_board(b+2, boards+i*19*19); - boards[col + 19*(row + i*19)] = 0; - - int flip = rand()%2; - int rotate = rand()%4; - image in = float_to_image(19, 19, 1, boards+i*19*19); - image out = float_to_image(19, 19, 1, labels+i*19*19); - if(flip){ - flip_image(in); - flip_image(out); - } - rotate_image_cw(in, rotate); - rotate_image_cw(out, rotate); - } -} - - -void train_go(char *cfgfile, char *weightfile) -{ - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - - char *backup_directory = "/home/pjreddie/backup/"; - - char buff[256]; - float *board = calloc(19*19*net.batch, sizeof(float)); - float *move = calloc(19*19*net.batch, sizeof(float)); - moves m = load_go_moves("/home/pjreddie/backup/go.train"); - //moves m = load_go_moves("games.txt"); - - int N = m.n; - int epoch = (*net.seen)/N; - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - clock_t time=clock(); - - random_go_moves(m, board, move, net.batch); - float loss = train_network_datum(net, board, move) / net.batch; - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.95 + loss*.05; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory,base, epoch); - save_weights(net, buff); - - } - if(get_current_batch(net)%100 == 0){ - char buff[256]; - sprintf(buff, "%s/%s.backup",backup_directory,base); - save_weights(net, buff); - } - if(get_current_batch(net)%10000 == 0){ - char buff[256]; - sprintf(buff, "%s/%s_%d.backup",backup_directory,base,get_current_batch(net)); - save_weights(net, buff); - } - } - sprintf(buff, "%s/%s.weights", backup_directory, base); - save_weights(net, buff); - - free_network(net); - free(base); -} - -void propagate_liberty(float *board, int *lib, int *visited, int row, int col, int side) -{ - if (row < 0 || row > 18 || col < 0 || col > 18) return; - int index = row*19 + col; - if (board[index] != side) return; - if (visited[index]) return; - visited[index] = 1; - lib[index] += 1; - propagate_liberty(board, lib, visited, row+1, col, side); - propagate_liberty(board, lib, visited, row-1, col, side); - propagate_liberty(board, lib, visited, row, col+1, side); - propagate_liberty(board, lib, visited, row, col-1, side); -} - - -int *calculate_liberties(float *board) -{ - int *lib = calloc(19*19, sizeof(int)); - int visited[361]; - int i, j; - for(j = 0; j < 19; ++j){ - for(i = 0; i < 19; ++i){ - memset(visited, 0, 19*19*sizeof(int)); - int index = j*19 + i; - if(board[index] == 0){ - if ((i > 0) && board[index - 1]) propagate_liberty(board, lib, visited, j, i-1, board[index-1]); - if ((i < 18) && board[index + 1]) propagate_liberty(board, lib, visited, j, i+1, board[index+1]); - if ((j > 0) && board[index - 19]) propagate_liberty(board, lib, visited, j-1, i, board[index-19]); - if ((j < 18) && board[index + 19]) propagate_liberty(board, lib, visited, j+1, i, board[index+19]); - } - } - } - return lib; -} - -void print_board(float *board, int swap, int *indexes) -{ - //FILE *stream = stdout; - FILE *stream = stderr; - int i,j,n; - fprintf(stream, "\n\n"); - fprintf(stream, " "); - for(i = 0; i < 19; ++i){ - fprintf(stream, "%c ", 'A' + i + 1*(i > 7 && noi)); - } - fprintf(stream, "\n"); - for(j = 0; j < 19; ++j){ - fprintf(stream, "%2d", (inverted) ? 19-j : j+1); - for(i = 0; i < 19; ++i){ - int index = j*19 + i; - if(indexes){ - int found = 0; - for(n = 0; n < nind; ++n){ - if(index == indexes[n]){ - found = 1; - /* - if(n == 0) fprintf(stream, "\uff11"); - else if(n == 1) fprintf(stream, "\uff12"); - else if(n == 2) fprintf(stream, "\uff13"); - else if(n == 3) fprintf(stream, "\uff14"); - else if(n == 4) fprintf(stream, "\uff15"); - */ - if(n == 0) fprintf(stream, " 1"); - else if(n == 1) fprintf(stream, " 2"); - else if(n == 2) fprintf(stream, " 3"); - else if(n == 3) fprintf(stream, " 4"); - else if(n == 4) fprintf(stream, " 5"); - } - } - if(found) continue; - } - //if(board[index]*-swap > 0) fprintf(stream, "\u25C9 "); - //else if(board[index]*-swap < 0) fprintf(stream, "\u25EF "); - if(board[index]*-swap > 0) fprintf(stream, " O"); - else if(board[index]*-swap < 0) fprintf(stream, " X"); - else fprintf(stream, " "); - } - fprintf(stream, "\n"); - } -} - -void flip_board(float *board) -{ - int i; - for(i = 0; i < 19*19; ++i){ - board[i] = -board[i]; - } -} - -void predict_move(network net, float *board, float *move, int multi) -{ - float *output = network_predict(net, board); - copy_cpu(19*19, output, 1, move, 1); - int i; - if(multi){ - image bim = float_to_image(19, 19, 1, board); - for(i = 1; i < 8; ++i){ - rotate_image_cw(bim, i); - if(i >= 4) flip_image(bim); - - float *output = network_predict(net, board); - image oim = float_to_image(19, 19, 1, output); - - if(i >= 4) flip_image(oim); - rotate_image_cw(oim, -i); - - axpy_cpu(19*19, 1, output, 1, move, 1); - - if(i >= 4) flip_image(bim); - rotate_image_cw(bim, -i); - } - scal_cpu(19*19, 1./8., move, 1); - } - for(i = 0; i < 19*19; ++i){ - if(board[i]) move[i] = 0; - } -} - -void remove_connected(float *b, int *lib, int p, int r, int c) -{ - if (r < 0 || r >= 19 || c < 0 || c >= 19) return; - if (b[r*19 + c] != p) return; - if (lib[r*19 + c] != 1) return; - b[r*19 + c] = 0; - remove_connected(b, lib, p, r+1, c); - remove_connected(b, lib, p, r-1, c); - remove_connected(b, lib, p, r, c+1); - remove_connected(b, lib, p, r, c-1); -} - - -void move_go(float *b, int p, int r, int c) -{ - int *l = calculate_liberties(b); - b[r*19 + c] = p; - remove_connected(b, l, -p, r+1, c); - remove_connected(b, l, -p, r-1, c); - remove_connected(b, l, -p, r, c+1); - remove_connected(b, l, -p, r, c-1); - free(l); -} - -int makes_safe_go(float *b, int *lib, int p, int r, int c){ - if (r < 0 || r >= 19 || c < 0 || c >= 19) return 0; - if (b[r*19 + c] == -p){ - if (lib[r*19 + c] > 1) return 0; - else return 1; - } - if (b[r*19 + c] == 0) return 1; - if (lib[r*19 + c] > 1) return 1; - return 0; -} - -int suicide_go(float *b, int p, int r, int c) -{ - int *l = calculate_liberties(b); - int safe = 0; - safe = safe || makes_safe_go(b, l, p, r+1, c); - safe = safe || makes_safe_go(b, l, p, r-1, c); - safe = safe || makes_safe_go(b, l, p, r, c+1); - safe = safe || makes_safe_go(b, l, p, r, c-1); - free(l); - return !safe; -} - -int legal_go(float *b, char *ko, int p, int r, int c) -{ - if (b[r*19 + c]) return 0; - char curr[91]; - char next[91]; - board_to_string(curr, b); - move_go(b, p, r, c); - board_to_string(next, b); - string_to_board(curr, b); - if(memcmp(next, ko, 91) == 0) return 0; - return 1; -} - -int generate_move(network net, int player, float *board, int multi, float thresh, float temp, char *ko, int print) -{ - int i, j; - for(i = 0; i < net.n; ++i) net.layers[i].temperature = temp; - - float move[361]; - if (player < 0) flip_board(board); - predict_move(net, board, move, multi); - if (player < 0) flip_board(board); - - - for(i = 0; i < 19; ++i){ - for(j = 0; j < 19; ++j){ - if (!legal_go(board, ko, player, i, j)) move[i*19 + j] = 0; - } - } - - int indexes[nind]; - top_k(move, 19*19, nind, indexes); - if(thresh > move[indexes[0]]) thresh = move[indexes[nind-1]]; - - for(i = 0; i < 19; ++i){ - for(j = 0; j < 19; ++j){ - if (move[i*19 + j] < thresh) move[i*19 + j] = 0; - } - } - - - int max = max_index(move, 19*19); - int row = max / 19; - int col = max % 19; - int index = sample_array(move, 19*19); - - if(print){ - top_k(move, 19*19, nind, indexes); - for(i = 0; i < nind; ++i){ - if (!move[indexes[i]]) indexes[i] = -1; - } - print_board(board, player, indexes); - for(i = 0; i < nind; ++i){ - fprintf(stderr, "%d: %f\n", i+1, move[indexes[i]]); - } - } - - if(suicide_go(board, player, row, col)){ - return -1; - } - if(suicide_go(board, player, index/19, index%19)) index = max; - return index; -} - -void valid_go(char *cfgfile, char *weightfile, int multi) -{ - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - - float *board = calloc(19*19, sizeof(float)); - float *move = calloc(19*19, sizeof(float)); - moves m = load_go_moves("/home/pjreddie/backup/go.test"); - - int N = m.n; - int i; - int correct = 0; - for(i = 0; i = 'A' && c <= 'Z') c = c - 'A'; - if(c >= 'a' && c <= 'z') c = c - 'a'; - if(c >= 8) --c; - r = 19 - r; - fprintf(stderr, "move: %d %d\n", r, c); - - char *swap = two; - two = one; - one = swap; - move_go(board, player, r, c); - board_to_string(one, board); - - printf("=%s \n\n", ids); - print_board(board, 1, 0); - } else if (!strcmp(buff, "genmove")){ - char color[256]; - scanf("%s", color); - int player = (color[0] == 'b' || color[0] == 'B') ? 1 : -1; - - int index = generate_move(net, player, board, multi, .1, .7, two, 1); - if(passed || index < 0){ - printf("=%s pass\n\n", ids); - passed = 0; - } else { - int row = index / 19; - int col = index % 19; - - char *swap = two; - two = one; - one = swap; - - move_go(board, player, row, col); - board_to_string(one, board); - row = 19 - row; - if (col >= 8) ++col; - printf("=%s %c%d\n\n", ids, 'A' + col, row); - print_board(board, 1, 0); - } - - } else if (!strcmp(buff, "p")){ - //print_board(board, 1, 0); - } else if (!strcmp(buff, "final_status_list")){ - char type[256]; - scanf("%s", type); - fprintf(stderr, "final_status\n"); - char *line = fgetl(stdin); - free(line); - if(type[0] == 'd' || type[0] == 'D'){ - FILE *f = fopen("game.txt", "w"); - int i, j; - int count = 2; - fprintf(f, "boardsize 19\n"); - fprintf(f, "clear_board\n"); - for(j = 0; j < 19; ++j){ - for(i = 0; i < 19; ++i){ - if(board[j*19 + i] == 1) fprintf(f, "play black %c%d\n", 'A'+i+(i>=8), 19-j); - if(board[j*19 + i] == -1) fprintf(f, "play white %c%d\n", 'A'+i+(i>=8), 19-j); - if(board[j*19 + i]) ++count; - } - } - fprintf(f, "final_status_list dead\n"); - fclose(f); - FILE *p = popen("./gnugo --mode gtp < game.txt", "r"); - for(i = 0; i < count; ++i){ - free(fgetl(p)); - free(fgetl(p)); - } - char *l = 0; - while((l = fgetl(p))){ - printf("%s\n", l); - free(l); - } - } else { - printf("?%s unknown command\n\n", ids); - } - } else { - char *line = fgetl(stdin); - free(line); - printf("?%s unknown command\n\n", ids); - } - fflush(stdout); - fflush(stderr); - } -} - -void test_go(char *cfg, char *weights, int multi) -{ - network net = parse_network_cfg(cfg); - if(weights){ - load_weights(&net, weights); - } - srand(time(0)); - set_batch_network(&net, 1); - float *board = calloc(19*19, sizeof(float)); - float *move = calloc(19*19, sizeof(float)); - int color = 1; - while(1){ - float *output = network_predict(net, board); - copy_cpu(19*19, output, 1, move, 1); - int i; - if(multi){ - image bim = float_to_image(19, 19, 1, board); - for(i = 1; i < 8; ++i){ - rotate_image_cw(bim, i); - if(i >= 4) flip_image(bim); - - float *output = network_predict(net, board); - image oim = float_to_image(19, 19, 1, output); - - if(i >= 4) flip_image(oim); - rotate_image_cw(oim, -i); - - axpy_cpu(19*19, 1, output, 1, move, 1); - - if(i >= 4) flip_image(bim); - rotate_image_cw(bim, -i); - } - scal_cpu(19*19, 1./8., move, 1); - } - for(i = 0; i < 19*19; ++i){ - if(board[i]) move[i] = 0; - } - - int indexes[nind]; - int row, col; - top_k(move, 19*19, nind, indexes); - print_board(board, color, indexes); - for(i = 0; i < nind; ++i){ - int index = indexes[i]; - row = index / 19; - col = index % 19; - printf("%d: %c %d, %.2f%%\n", i+1, col + 'A' + 1*(col > 7 && noi), (inverted)?19 - row : row+1, move[index]*100); - } - //if(color == 1) printf("\u25EF Enter move: "); - //else printf("\u25C9 Enter move: "); - if(color == 1) printf("X Enter move: "); - else printf("O Enter move: "); - - char c; - char *line = fgetl(stdin); - int picked = 1; - int dnum = sscanf(line, "%d", &picked); - int cnum = sscanf(line, "%c", &c); - if (strlen(line) == 0 || dnum) { - --picked; - if (picked < nind){ - int index = indexes[picked]; - row = index / 19; - col = index % 19; - board[row*19 + col] = 1; - } - } else if (cnum){ - if (c <= 'T' && c >= 'A'){ - int num = sscanf(line, "%c %d", &c, &row); - row = (inverted)?19 - row : row-1; - col = c - 'A'; - if (col > 7 && noi) col -= 1; - if (num == 2) board[row*19 + col] = 1; - } else if (c == 'p') { - // Pass - } else if(c=='b' || c == 'w'){ - char g; - int num = sscanf(line, "%c %c %d", &g, &c, &row); - row = (inverted)?19 - row : row-1; - col = c - 'A'; - if (col > 7 && noi) col -= 1; - if (num == 3) board[row*19 + col] = (g == 'b') ? color : -color; - } else if(c == 'c'){ - char g; - int num = sscanf(line, "%c %c %d", &g, &c, &row); - row = (inverted)?19 - row : row-1; - col = c - 'A'; - if (col > 7 && noi) col -= 1; - if (num == 3) board[row*19 + col] = 0; - } - } - free(line); - flip_board(board); - color = -color; - } -} - -float score_game(float *board) -{ - FILE *f = fopen("game.txt", "w"); - int i, j; - int count = 3; - fprintf(f, "komi 6.5\n"); - fprintf(f, "boardsize 19\n"); - fprintf(f, "clear_board\n"); - for(j = 0; j < 19; ++j){ - for(i = 0; i < 19; ++i){ - if(board[j*19 + i] == 1) fprintf(f, "play black %c%d\n", 'A'+i+(i>=8), 19-j); - if(board[j*19 + i] == -1) fprintf(f, "play white %c%d\n", 'A'+i+(i>=8), 19-j); - if(board[j*19 + i]) ++count; - } - } - fprintf(f, "final_score\n"); - fclose(f); - FILE *p = popen("./gnugo --mode gtp < game.txt", "r"); - for(i = 0; i < count; ++i){ - free(fgetl(p)); - free(fgetl(p)); - } - char *l = 0; - float score = 0; - char player = 0; - while((l = fgetl(p))){ - fprintf(stderr, "%s \t", l); - int n = sscanf(l, "= %c+%f", &player, &score); - free(l); - if (n == 2) break; - } - if(player == 'W') score = -score; - pclose(p); - return score; -} - -void self_go(char *filename, char *weightfile, char *f2, char *w2, int multi) -{ - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - - network net2 = net; - if(f2){ - net2 = parse_network_cfg(f2); - if(w2){ - load_weights(&net2, w2); - } - } - srand(time(0)); - char boards[300][93]; - int count = 0; - set_batch_network(&net, 1); - set_batch_network(&net2, 1); - float *board = calloc(19*19, sizeof(float)); - char *one = calloc(91, sizeof(char)); - char *two = calloc(91, sizeof(char)); - int done = 0; - int player = 1; - int p1 = 0; - int p2 = 0; - int total = 0; - while(1){ - if (done || count >= 300){ - float score = score_game(board); - int i = (score > 0)? 0 : 1; - if((score > 0) == (total%2==0)) ++p1; - else ++p2; - ++total; - fprintf(stderr, "Total: %d, Player 1: %f, Player 2: %f\n", total, (float)p1/total, (float)p2/total); - int j; - for(; i < count; i += 2){ - for(j = 0; j < 93; ++j){ - printf("%c", boards[i][j]); - } - printf("\n"); - } - memset(board, 0, 19*19*sizeof(float)); - player = 1; - done = 0; - count = 0; - fflush(stdout); - fflush(stderr); - } - //print_board(board, 1, 0); - //sleep(1); - network use = ((total%2==0) == (player==1)) ? net : net2; - int index = generate_move(use, player, board, multi, .1, .7, two, 0); - if(index < 0){ - done = 1; - continue; - } - int row = index / 19; - int col = index % 19; - - char *swap = two; - two = one; - one = swap; - - if(player < 0) flip_board(board); - boards[count][0] = row; - boards[count][1] = col; - board_to_string(boards[count] + 2, board); - if(player < 0) flip_board(board); - ++count; - - move_go(board, player, row, col); - board_to_string(one, board); - - player = -player; - } -} - -void run_go(int argc, char **argv) -{ - //boards_go(); - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *c2 = (argc > 5) ? argv[5] : 0; - char *w2 = (argc > 6) ? argv[6] : 0; - int multi = find_arg(argc, argv, "-multi"); - if(0==strcmp(argv[2], "train")) train_go(cfg, weights); - else if(0==strcmp(argv[2], "valid")) valid_go(cfg, weights, multi); - else if(0==strcmp(argv[2], "self")) self_go(cfg, weights, c2, w2, multi); - else if(0==strcmp(argv[2], "test")) test_go(cfg, weights, multi); - else if(0==strcmp(argv[2], "engine")) engine_go(cfg, weights, multi); -} - - diff --git a/image.darknet/src/gru_layer.c b/image.darknet/src/gru_layer.c index b78e868..b6601d8 100644 --- a/image.darknet/src/gru_layer.c +++ b/image.darknet/src/gru_layer.c @@ -26,7 +26,7 @@ static void increment_layer(layer *l, int steps) #endif } -layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_normalize) +layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_normalize, int adam) { fprintf(stderr, "GRU Layer: %d inputs, %d outputs\n", inputs, outputs); batch = batch / steps; @@ -36,39 +36,37 @@ layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_no l.steps = steps; l.inputs = inputs; - l.input_z_layer = malloc(sizeof(layer)); + l.uz = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_z_layer) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize); - l.input_z_layer->batch = batch; + *(l.uz) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.uz->batch = batch; - l.state_z_layer = malloc(sizeof(layer)); + l.wz = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.state_z_layer) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize); - l.state_z_layer->batch = batch; + *(l.wz) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wz->batch = batch; - - - l.input_r_layer = malloc(sizeof(layer)); + l.ur = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_r_layer) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize); - l.input_r_layer->batch = batch; + *(l.ur) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.ur->batch = batch; - l.state_r_layer = malloc(sizeof(layer)); + l.wr = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.state_r_layer) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize); - l.state_r_layer->batch = batch; + *(l.wr) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wr->batch = batch; - l.input_h_layer = malloc(sizeof(layer)); + l.uh = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_h_layer) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize); - l.input_h_layer->batch = batch; + *(l.uh) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.uh->batch = batch; - l.state_h_layer = malloc(sizeof(layer)); + l.wh = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.state_h_layer) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize); - l.state_h_layer->batch = batch; + *(l.wh) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wh->batch = batch; l.batch_normalize = batch_normalize; @@ -94,68 +92,80 @@ layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_no l.backward_gpu = backward_gru_layer_gpu; l.update_gpu = update_gru_layer_gpu; - l.forgot_state_gpu = cuda_make_array(l.output, batch*outputs); - l.forgot_delta_gpu = cuda_make_array(l.output, batch*outputs); - l.prev_state_gpu = cuda_make_array(l.output, batch*outputs); - l.state_gpu = cuda_make_array(l.output, batch*outputs); - l.output_gpu = cuda_make_array(l.output, batch*outputs*steps); - l.delta_gpu = cuda_make_array(l.delta, batch*outputs*steps); - l.r_gpu = cuda_make_array(l.output_gpu, batch*outputs); - l.z_gpu = cuda_make_array(l.output_gpu, batch*outputs); - l.h_gpu = cuda_make_array(l.output_gpu, batch*outputs); + l.forgot_state_gpu = cuda_make_array(0, batch*outputs); + l.forgot_delta_gpu = cuda_make_array(0, batch*outputs); + l.prev_state_gpu = cuda_make_array(0, batch*outputs); + l.state_gpu = cuda_make_array(0, batch*outputs); + l.output_gpu = cuda_make_array(0, batch*outputs*steps); + l.delta_gpu = cuda_make_array(0, batch*outputs*steps); + l.r_gpu = cuda_make_array(0, batch*outputs); + l.z_gpu = cuda_make_array(0, batch*outputs); + l.h_gpu = cuda_make_array(0, batch*outputs); + +#ifdef CUDNN + cudnnSetTensor4dDescriptor(l.uz->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.uz->out_c, l.uz->out_h, l.uz->out_w); + cudnnSetTensor4dDescriptor(l.uh->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.uh->out_c, l.uh->out_h, l.uh->out_w); + cudnnSetTensor4dDescriptor(l.ur->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.ur->out_c, l.ur->out_h, l.ur->out_w); + cudnnSetTensor4dDescriptor(l.wz->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wz->out_c, l.wz->out_h, l.wz->out_w); + cudnnSetTensor4dDescriptor(l.wh->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wh->out_c, l.wh->out_h, l.wh->out_w); + cudnnSetTensor4dDescriptor(l.wr->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wr->out_c, l.wr->out_h, l.wr->out_w); +#endif #endif return l; } -void update_gru_layer(layer l, int batch, float learning_rate, float momentum, float decay) +void update_gru_layer(layer l, update_args a) { - update_connected_layer(*(l.input_layer), batch, learning_rate, momentum, decay); - update_connected_layer(*(l.self_layer), batch, learning_rate, momentum, decay); - update_connected_layer(*(l.output_layer), batch, learning_rate, momentum, decay); + update_connected_layer(*(l.ur), a); + update_connected_layer(*(l.uz), a); + update_connected_layer(*(l.uh), a); + update_connected_layer(*(l.wr), a); + update_connected_layer(*(l.wz), a); + update_connected_layer(*(l.wh), a); } -void forward_gru_layer(layer l, network_state state) +void forward_gru_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; - layer input_z_layer = *(l.input_z_layer); - layer input_r_layer = *(l.input_r_layer); - layer input_h_layer = *(l.input_h_layer); - - layer state_z_layer = *(l.state_z_layer); - layer state_r_layer = *(l.state_r_layer); - layer state_h_layer = *(l.state_h_layer); - - fill_cpu(l.outputs * l.batch * l.steps, 0, input_z_layer.delta, 1); - fill_cpu(l.outputs * l.batch * l.steps, 0, input_r_layer.delta, 1); - fill_cpu(l.outputs * l.batch * l.steps, 0, input_h_layer.delta, 1); - - fill_cpu(l.outputs * l.batch * l.steps, 0, state_z_layer.delta, 1); - fill_cpu(l.outputs * l.batch * l.steps, 0, state_r_layer.delta, 1); - fill_cpu(l.outputs * l.batch * l.steps, 0, state_h_layer.delta, 1); - if(state.train) { + layer uz = *(l.uz); + layer ur = *(l.ur); + layer uh = *(l.uh); + + layer wz = *(l.wz); + layer wr = *(l.wr); + layer wh = *(l.wh); + + fill_cpu(l.outputs * l.batch * l.steps, 0, uz.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, ur.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, uh.delta, 1); + + fill_cpu(l.outputs * l.batch * l.steps, 0, wz.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wr.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wh.delta, 1); + if(net.train) { fill_cpu(l.outputs * l.batch * l.steps, 0, l.delta, 1); copy_cpu(l.outputs*l.batch, l.state, 1, l.prev_state, 1); } for (i = 0; i < l.steps; ++i) { s.input = l.state; - forward_connected_layer(state_z_layer, s); - forward_connected_layer(state_r_layer, s); + forward_connected_layer(wz, s); + forward_connected_layer(wr, s); - s.input = state.input; - forward_connected_layer(input_z_layer, s); - forward_connected_layer(input_r_layer, s); - forward_connected_layer(input_h_layer, s); + s.input = net.input; + forward_connected_layer(uz, s); + forward_connected_layer(ur, s); + forward_connected_layer(uh, s); - copy_cpu(l.outputs*l.batch, input_z_layer.output, 1, l.z_cpu, 1); - axpy_cpu(l.outputs*l.batch, 1, state_z_layer.output, 1, l.z_cpu, 1); + copy_cpu(l.outputs*l.batch, uz.output, 1, l.z_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, wz.output, 1, l.z_cpu, 1); - copy_cpu(l.outputs*l.batch, input_r_layer.output, 1, l.r_cpu, 1); - axpy_cpu(l.outputs*l.batch, 1, state_r_layer.output, 1, l.r_cpu, 1); + copy_cpu(l.outputs*l.batch, ur.output, 1, l.r_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, wr.output, 1, l.r_cpu, 1); activate_array(l.z_cpu, l.outputs*l.batch, LOGISTIC); activate_array(l.r_cpu, l.outputs*l.batch, LOGISTIC); @@ -164,34 +174,34 @@ void forward_gru_layer(layer l, network_state state) mul_cpu(l.outputs*l.batch, l.r_cpu, 1, l.forgot_state, 1); s.input = l.forgot_state; - forward_connected_layer(state_h_layer, s); + forward_connected_layer(wh, s); - copy_cpu(l.outputs*l.batch, input_h_layer.output, 1, l.h_cpu, 1); - axpy_cpu(l.outputs*l.batch, 1, state_h_layer.output, 1, l.h_cpu, 1); + copy_cpu(l.outputs*l.batch, uh.output, 1, l.h_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, wh.output, 1, l.h_cpu, 1); - #ifdef USET - activate_array(l.h_cpu, l.outputs*l.batch, TANH); - #else - activate_array(l.h_cpu, l.outputs*l.batch, LOGISTIC); - #endif + if(l.tanh){ + activate_array(l.h_cpu, l.outputs*l.batch, TANH); + } else { + activate_array(l.h_cpu, l.outputs*l.batch, LOGISTIC); + } weighted_sum_cpu(l.state, l.h_cpu, l.z_cpu, l.outputs*l.batch, l.output); copy_cpu(l.outputs*l.batch, l.output, 1, l.state, 1); - state.input += l.inputs*l.batch; + net.input += l.inputs*l.batch; l.output += l.outputs*l.batch; - increment_layer(&input_z_layer, 1); - increment_layer(&input_r_layer, 1); - increment_layer(&input_h_layer, 1); + increment_layer(&uz, 1); + increment_layer(&ur, 1); + increment_layer(&uh, 1); - increment_layer(&state_z_layer, 1); - increment_layer(&state_r_layer, 1); - increment_layer(&state_h_layer, 1); + increment_layer(&wz, 1); + increment_layer(&wr, 1); + increment_layer(&wh, 1); } } -void backward_gru_layer(layer l, network_state state) +void backward_gru_layer(layer l, network net) { } @@ -205,191 +215,192 @@ void push_gru_layer(layer l) { } -void update_gru_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay) +void update_gru_layer_gpu(layer l, update_args a) { - update_connected_layer_gpu(*(l.input_r_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.input_z_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.input_h_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.state_r_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.state_z_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.state_h_layer), batch, learning_rate, momentum, decay); + update_connected_layer_gpu(*(l.ur), a); + update_connected_layer_gpu(*(l.uz), a); + update_connected_layer_gpu(*(l.uh), a); + update_connected_layer_gpu(*(l.wr), a); + update_connected_layer_gpu(*(l.wz), a); + update_connected_layer_gpu(*(l.wh), a); } -void forward_gru_layer_gpu(layer l, network_state state) +void forward_gru_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = {0}; + s.train = net.train; int i; - layer input_z_layer = *(l.input_z_layer); - layer input_r_layer = *(l.input_r_layer); - layer input_h_layer = *(l.input_h_layer); - - layer state_z_layer = *(l.state_z_layer); - layer state_r_layer = *(l.state_r_layer); - layer state_h_layer = *(l.state_h_layer); - - fill_ongpu(l.outputs * l.batch * l.steps, 0, input_z_layer.delta_gpu, 1); - fill_ongpu(l.outputs * l.batch * l.steps, 0, input_r_layer.delta_gpu, 1); - fill_ongpu(l.outputs * l.batch * l.steps, 0, input_h_layer.delta_gpu, 1); - - fill_ongpu(l.outputs * l.batch * l.steps, 0, state_z_layer.delta_gpu, 1); - fill_ongpu(l.outputs * l.batch * l.steps, 0, state_r_layer.delta_gpu, 1); - fill_ongpu(l.outputs * l.batch * l.steps, 0, state_h_layer.delta_gpu, 1); - if(state.train) { - fill_ongpu(l.outputs * l.batch * l.steps, 0, l.delta_gpu, 1); - copy_ongpu(l.outputs*l.batch, l.state_gpu, 1, l.prev_state_gpu, 1); + layer uz = *(l.uz); + layer ur = *(l.ur); + layer uh = *(l.uh); + + layer wz = *(l.wz); + layer wr = *(l.wr); + layer wh = *(l.wh); + + fill_gpu(l.outputs * l.batch * l.steps, 0, uz.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, ur.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, uh.delta_gpu, 1); + + fill_gpu(l.outputs * l.batch * l.steps, 0, wz.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wr.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wh.delta_gpu, 1); + if(net.train) { + fill_gpu(l.outputs * l.batch * l.steps, 0, l.delta_gpu, 1); + copy_gpu(l.outputs*l.batch, l.state_gpu, 1, l.prev_state_gpu, 1); } for (i = 0; i < l.steps; ++i) { - s.input = l.state_gpu; - forward_connected_layer_gpu(state_z_layer, s); - forward_connected_layer_gpu(state_r_layer, s); + s.input_gpu = l.state_gpu; + forward_connected_layer_gpu(wz, s); + forward_connected_layer_gpu(wr, s); - s.input = state.input; - forward_connected_layer_gpu(input_z_layer, s); - forward_connected_layer_gpu(input_r_layer, s); - forward_connected_layer_gpu(input_h_layer, s); + s.input_gpu = net.input_gpu; + forward_connected_layer_gpu(uz, s); + forward_connected_layer_gpu(ur, s); + forward_connected_layer_gpu(uh, s); + copy_gpu(l.outputs*l.batch, uz.output_gpu, 1, l.z_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wz.output_gpu, 1, l.z_gpu, 1); - copy_ongpu(l.outputs*l.batch, input_z_layer.output_gpu, 1, l.z_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_z_layer.output_gpu, 1, l.z_gpu, 1); + copy_gpu(l.outputs*l.batch, ur.output_gpu, 1, l.r_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wr.output_gpu, 1, l.r_gpu, 1); - copy_ongpu(l.outputs*l.batch, input_r_layer.output_gpu, 1, l.r_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_r_layer.output_gpu, 1, l.r_gpu, 1); + activate_array_gpu(l.z_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.r_gpu, l.outputs*l.batch, LOGISTIC); - activate_array_ongpu(l.z_gpu, l.outputs*l.batch, LOGISTIC); - activate_array_ongpu(l.r_gpu, l.outputs*l.batch, LOGISTIC); + copy_gpu(l.outputs*l.batch, l.state_gpu, 1, l.forgot_state_gpu, 1); + mul_gpu(l.outputs*l.batch, l.r_gpu, 1, l.forgot_state_gpu, 1); - copy_ongpu(l.outputs*l.batch, l.state_gpu, 1, l.forgot_state_gpu, 1); - mul_ongpu(l.outputs*l.batch, l.r_gpu, 1, l.forgot_state_gpu, 1); + s.input_gpu = l.forgot_state_gpu; + forward_connected_layer_gpu(wh, s); - s.input = l.forgot_state_gpu; - forward_connected_layer_gpu(state_h_layer, s); + copy_gpu(l.outputs*l.batch, uh.output_gpu, 1, l.h_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wh.output_gpu, 1, l.h_gpu, 1); - copy_ongpu(l.outputs*l.batch, input_h_layer.output_gpu, 1, l.h_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_h_layer.output_gpu, 1, l.h_gpu, 1); - - #ifdef USET - activate_array_ongpu(l.h_gpu, l.outputs*l.batch, TANH); - #else - activate_array_ongpu(l.h_gpu, l.outputs*l.batch, LOGISTIC); - #endif + if(l.tanh){ + activate_array_gpu(l.h_gpu, l.outputs*l.batch, TANH); + } else { + activate_array_gpu(l.h_gpu, l.outputs*l.batch, LOGISTIC); + } weighted_sum_gpu(l.state_gpu, l.h_gpu, l.z_gpu, l.outputs*l.batch, l.output_gpu); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.state_gpu, 1); - copy_ongpu(l.outputs*l.batch, l.output_gpu, 1, l.state_gpu, 1); - - state.input += l.inputs*l.batch; + net.input_gpu += l.inputs*l.batch; l.output_gpu += l.outputs*l.batch; - increment_layer(&input_z_layer, 1); - increment_layer(&input_r_layer, 1); - increment_layer(&input_h_layer, 1); + increment_layer(&uz, 1); + increment_layer(&ur, 1); + increment_layer(&uh, 1); - increment_layer(&state_z_layer, 1); - increment_layer(&state_r_layer, 1); - increment_layer(&state_h_layer, 1); + increment_layer(&wz, 1); + increment_layer(&wr, 1); + increment_layer(&wh, 1); } } -void backward_gru_layer_gpu(layer l, network_state state) +void backward_gru_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = {0}; + s.train = net.train; int i; - layer input_z_layer = *(l.input_z_layer); - layer input_r_layer = *(l.input_r_layer); - layer input_h_layer = *(l.input_h_layer); + layer uz = *(l.uz); + layer ur = *(l.ur); + layer uh = *(l.uh); - layer state_z_layer = *(l.state_z_layer); - layer state_r_layer = *(l.state_r_layer); - layer state_h_layer = *(l.state_h_layer); + layer wz = *(l.wz); + layer wr = *(l.wr); + layer wh = *(l.wh); - increment_layer(&input_z_layer, l.steps - 1); - increment_layer(&input_r_layer, l.steps - 1); - increment_layer(&input_h_layer, l.steps - 1); + increment_layer(&uz, l.steps - 1); + increment_layer(&ur, l.steps - 1); + increment_layer(&uh, l.steps - 1); - increment_layer(&state_z_layer, l.steps - 1); - increment_layer(&state_r_layer, l.steps - 1); - increment_layer(&state_h_layer, l.steps - 1); + increment_layer(&wz, l.steps - 1); + increment_layer(&wr, l.steps - 1); + increment_layer(&wh, l.steps - 1); - state.input += l.inputs*l.batch*(l.steps-1); - if(state.delta) state.delta += l.inputs*l.batch*(l.steps-1); + net.input_gpu += l.inputs*l.batch*(l.steps-1); + if(net.delta_gpu) net.delta_gpu += l.inputs*l.batch*(l.steps-1); l.output_gpu += l.outputs*l.batch*(l.steps-1); l.delta_gpu += l.outputs*l.batch*(l.steps-1); + float *end_state = l.output_gpu; for (i = l.steps-1; i >= 0; --i) { - if(i != 0) copy_ongpu(l.outputs*l.batch, l.output_gpu - l.outputs*l.batch, 1, l.prev_state_gpu, 1); + if(i != 0) copy_gpu(l.outputs*l.batch, l.output_gpu - l.outputs*l.batch, 1, l.state_gpu, 1); + else copy_gpu(l.outputs*l.batch, l.prev_state_gpu, 1, l.state_gpu, 1); float *prev_delta_gpu = (i == 0) ? 0 : l.delta_gpu - l.outputs*l.batch; - copy_ongpu(l.outputs*l.batch, input_z_layer.output_gpu, 1, l.z_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_z_layer.output_gpu, 1, l.z_gpu, 1); - - copy_ongpu(l.outputs*l.batch, input_r_layer.output_gpu, 1, l.r_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_r_layer.output_gpu, 1, l.r_gpu, 1); - - activate_array_ongpu(l.z_gpu, l.outputs*l.batch, LOGISTIC); - activate_array_ongpu(l.r_gpu, l.outputs*l.batch, LOGISTIC); - - copy_ongpu(l.outputs*l.batch, input_h_layer.output_gpu, 1, l.h_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_h_layer.output_gpu, 1, l.h_gpu, 1); - - #ifdef USET - activate_array_ongpu(l.h_gpu, l.outputs*l.batch, TANH); - #else - activate_array_ongpu(l.h_gpu, l.outputs*l.batch, LOGISTIC); - #endif - - weighted_delta_gpu(l.prev_state_gpu, l.h_gpu, l.z_gpu, prev_delta_gpu, input_h_layer.delta_gpu, input_z_layer.delta_gpu, l.outputs*l.batch, l.delta_gpu); - - #ifdef USET - gradient_array_ongpu(l.h_gpu, l.outputs*l.batch, TANH, input_h_layer.delta_gpu); - #else - gradient_array_ongpu(l.h_gpu, l.outputs*l.batch, LOGISTIC, input_h_layer.delta_gpu); - #endif - - copy_ongpu(l.outputs*l.batch, input_h_layer.delta_gpu, 1, state_h_layer.delta_gpu, 1); - - copy_ongpu(l.outputs*l.batch, l.prev_state_gpu, 1, l.forgot_state_gpu, 1); - mul_ongpu(l.outputs*l.batch, l.r_gpu, 1, l.forgot_state_gpu, 1); - fill_ongpu(l.outputs*l.batch, 0, l.forgot_delta_gpu, 1); - - s.input = l.forgot_state_gpu; - s.delta = l.forgot_delta_gpu; - - backward_connected_layer_gpu(state_h_layer, s); + copy_gpu(l.outputs*l.batch, uz.output_gpu, 1, l.z_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wz.output_gpu, 1, l.z_gpu, 1); + + copy_gpu(l.outputs*l.batch, ur.output_gpu, 1, l.r_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wr.output_gpu, 1, l.r_gpu, 1); + + activate_array_gpu(l.z_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.r_gpu, l.outputs*l.batch, LOGISTIC); + + copy_gpu(l.outputs*l.batch, uh.output_gpu, 1, l.h_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wh.output_gpu, 1, l.h_gpu, 1); + + if(l.tanh){ + activate_array_gpu(l.h_gpu, l.outputs*l.batch, TANH); + } else { + activate_array_gpu(l.h_gpu, l.outputs*l.batch, LOGISTIC); + } + + weighted_delta_gpu(l.state_gpu, l.h_gpu, l.z_gpu, prev_delta_gpu, uh.delta_gpu, uz.delta_gpu, l.outputs*l.batch, l.delta_gpu); + + if(l.tanh){ + gradient_array_gpu(l.h_gpu, l.outputs*l.batch, TANH, uh.delta_gpu); + } else { + gradient_array_gpu(l.h_gpu, l.outputs*l.batch, LOGISTIC, uh.delta_gpu); + } + + copy_gpu(l.outputs*l.batch, uh.delta_gpu, 1, wh.delta_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.state_gpu, 1, l.forgot_state_gpu, 1); + mul_gpu(l.outputs*l.batch, l.r_gpu, 1, l.forgot_state_gpu, 1); + fill_gpu(l.outputs*l.batch, 0, l.forgot_delta_gpu, 1); + + s.input_gpu = l.forgot_state_gpu; + s.delta_gpu = l.forgot_delta_gpu; + + backward_connected_layer_gpu(wh, s); if(prev_delta_gpu) mult_add_into_gpu(l.outputs*l.batch, l.forgot_delta_gpu, l.r_gpu, prev_delta_gpu); - mult_add_into_gpu(l.outputs*l.batch, l.forgot_delta_gpu, l.prev_state_gpu, input_r_layer.delta_gpu); - - gradient_array_ongpu(l.r_gpu, l.outputs*l.batch, LOGISTIC, input_r_layer.delta_gpu); - copy_ongpu(l.outputs*l.batch, input_r_layer.delta_gpu, 1, state_r_layer.delta_gpu, 1); - - gradient_array_ongpu(l.z_gpu, l.outputs*l.batch, LOGISTIC, input_z_layer.delta_gpu); - copy_ongpu(l.outputs*l.batch, input_z_layer.delta_gpu, 1, state_z_layer.delta_gpu, 1); - - s.input = l.prev_state_gpu; - s.delta = prev_delta_gpu; - - backward_connected_layer_gpu(state_r_layer, s); - backward_connected_layer_gpu(state_z_layer, s); - - s.input = state.input; - s.delta = state.delta; - - backward_connected_layer_gpu(input_h_layer, s); - backward_connected_layer_gpu(input_r_layer, s); - backward_connected_layer_gpu(input_z_layer, s); - - - state.input -= l.inputs*l.batch; - if(state.delta) state.delta -= l.inputs*l.batch; + mult_add_into_gpu(l.outputs*l.batch, l.forgot_delta_gpu, l.state_gpu, ur.delta_gpu); + + gradient_array_gpu(l.r_gpu, l.outputs*l.batch, LOGISTIC, ur.delta_gpu); + copy_gpu(l.outputs*l.batch, ur.delta_gpu, 1, wr.delta_gpu, 1); + + gradient_array_gpu(l.z_gpu, l.outputs*l.batch, LOGISTIC, uz.delta_gpu); + copy_gpu(l.outputs*l.batch, uz.delta_gpu, 1, wz.delta_gpu, 1); + + s.input_gpu = l.state_gpu; + s.delta_gpu = prev_delta_gpu; + + backward_connected_layer_gpu(wr, s); + backward_connected_layer_gpu(wz, s); + + s.input_gpu = net.input_gpu; + s.delta_gpu = net.delta_gpu; + + backward_connected_layer_gpu(uh, s); + backward_connected_layer_gpu(ur, s); + backward_connected_layer_gpu(uz, s); + + + net.input_gpu -= l.inputs*l.batch; + if(net.delta_gpu) net.delta_gpu -= l.inputs*l.batch; l.output_gpu -= l.outputs*l.batch; l.delta_gpu -= l.outputs*l.batch; - increment_layer(&input_z_layer, -1); - increment_layer(&input_r_layer, -1); - increment_layer(&input_h_layer, -1); + increment_layer(&uz, -1); + increment_layer(&ur, -1); + increment_layer(&uh, -1); - increment_layer(&state_z_layer, -1); - increment_layer(&state_r_layer, -1); - increment_layer(&state_h_layer, -1); + increment_layer(&wz, -1); + increment_layer(&wr, -1); + increment_layer(&wh, -1); } + copy_gpu(l.outputs*l.batch, end_state, 1, l.state_gpu, 1); } #endif diff --git a/image.darknet/src/gru_layer.h b/image.darknet/src/gru_layer.h index 9e19cee..9067942 100644 --- a/image.darknet/src/gru_layer.h +++ b/image.darknet/src/gru_layer.h @@ -6,16 +6,16 @@ #include "layer.h" #include "network.h" -layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_normalize); +layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_normalize, int adam); -void forward_gru_layer(layer l, network_state state); -void backward_gru_layer(layer l, network_state state); -void update_gru_layer(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_gru_layer(layer l, network state); +void backward_gru_layer(layer l, network state); +void update_gru_layer(layer l, update_args a); #ifdef GPU -void forward_gru_layer_gpu(layer l, network_state state); -void backward_gru_layer_gpu(layer l, network_state state); -void update_gru_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_gru_layer_gpu(layer l, network state); +void backward_gru_layer_gpu(layer l, network state); +void update_gru_layer_gpu(layer l, update_args a); void push_gru_layer(layer l); void pull_gru_layer(layer l); #endif diff --git a/image.darknet/src/im2col.h b/image.darknet/src/im2col.h index f0ddeee..02c4247 100644 --- a/image.darknet/src/im2col.h +++ b/image.darknet/src/im2col.h @@ -7,7 +7,7 @@ void im2col_cpu(float* data_im, #ifdef GPU -void im2col_ongpu(float *im, +void im2col_gpu(float *im, int channels, int height, int width, int ksize, int stride, int pad,float *data_col); diff --git a/image.darknet/src/im2col_kernels.cu b/image.darknet/src/im2col_kernels.cu index d42d600..07b5e67 100644 --- a/image.darknet/src/im2col_kernels.cu +++ b/image.darknet/src/im2col_kernels.cu @@ -45,7 +45,7 @@ __global__ void im2col_gpu_kernel(const int n, const float* data_im, } } -void im2col_ongpu(float *im, +void im2col_gpu(float *im, int channels, int height, int width, int ksize, int stride, int pad, float *data_col){ // We are going to launch channels * height_col * width_col kernels, each diff --git a/image.darknet/src/image.c b/image.darknet/src/image.c index 5a90efd..4a2c6ba 100644 --- a/image.darknet/src/image.c +++ b/image.darknet/src/image.c @@ -10,12 +10,6 @@ #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#include "opencv2/imgproc/imgproc_c.h" -#endif - - int windows = 0; float colors[6][3] = { {1,0,1}, {0,0,1},{0,1,1},{0,1,0},{1,1,0},{1,0,0} }; @@ -31,6 +25,70 @@ float get_color(int c, int x, int max) return r; } +image mask_to_rgb(image mask) +{ + int n = mask.c; + image im = make_image(mask.w, mask.h, 3); + int i, j; + for(j = 0; j < n; ++j){ + int offset = j*123457 % n; + float red = get_color(2,offset,n); + float green = get_color(1,offset,n); + float blue = get_color(0,offset,n); + for(i = 0; i < im.w*im.h; ++i){ + im.data[i + 0*im.w*im.h] += mask.data[j*im.h*im.w + i]*red; + im.data[i + 1*im.w*im.h] += mask.data[j*im.h*im.w + i]*green; + im.data[i + 2*im.w*im.h] += mask.data[j*im.h*im.w + i]*blue; + } + } + return im; +} + +static float get_pixel(image m, int x, int y, int c) +{ + assert(x < m.w && y < m.h && c < m.c); + return m.data[c*m.h*m.w + y*m.w + x]; +} +static float get_pixel_extend(image m, int x, int y, int c) +{ + if(x < 0 || x >= m.w || y < 0 || y >= m.h) return 0; + /* + if(x < 0) x = 0; + if(x >= m.w) x = m.w-1; + if(y < 0) y = 0; + if(y >= m.h) y = m.h-1; + */ + if(c < 0 || c >= m.c) return 0; + return get_pixel(m, x, y, c); +} +static void set_pixel(image m, int x, int y, int c, float val) +{ + if (x < 0 || y < 0 || c < 0 || x >= m.w || y >= m.h || c >= m.c) return; + assert(x < m.w && y < m.h && c < m.c); + m.data[c*m.h*m.w + y*m.w + x] = val; +} +static void add_pixel(image m, int x, int y, int c, float val) +{ + assert(x < m.w && y < m.h && c < m.c); + m.data[c*m.h*m.w + y*m.w + x] += val; +} + +static float bilinear_interpolate(image im, float x, float y, int c) +{ + int ix = (int) floorf(x); + int iy = (int) floorf(y); + + float dx = x - ix; + float dy = y - iy; + + float val = (1-dy) * (1-dx) * get_pixel_extend(im, ix, iy, c) + + dy * (1-dx) * get_pixel_extend(im, ix, iy+1, c) + + (1-dy) * dx * get_pixel_extend(im, ix+1, iy, c) + + dy * dx * get_pixel_extend(im, ix+1, iy+1, c); + return val; +} + + void composite_image(image source, image dest, int dx, int dy) { int x,y,k; @@ -73,6 +131,7 @@ image tile_images(image a, image b, int dx) image get_label(image **characters, char *string, int size) { + size = size/10; if(size > 7) size = 7; image label = make_empty_image(0,0,0); while(*string){ @@ -177,23 +236,36 @@ image **load_alphabet() return alphabets; } -void draw_detections(image im, int num, float thresh, box *boxes, float **probs, char **names, image **alphabet, int classes) +void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes) { - int i; + int i,j; for(i = 0; i < num; ++i){ - int class = max_index(probs[i], classes); - float prob = probs[i][class]; - if(prob > thresh){ - - int width = im.h * .012; - - if(0){ - width = pow(prob, 1./2.)*10+1; - alphabet = 0; + char labelstr[4096] = {0}; + int class = -1; + for(j = 0; j < classes; ++j){ + if (dets[i].prob[j] > thresh){ + if (class < 0) { + strcat(labelstr, names[j]); + class = j; + } else { + strcat(labelstr, ", "); + strcat(labelstr, names[j]); + } + printf("%s: %.0f%%\n", names[j], dets[i].prob[j]*100); } + } + if(class >= 0){ + int width = im.h * .006; - printf("%s: %.0f%%\n", names[class], prob*100); + /* + if(0){ + width = pow(prob, 1./2.)*10+1; + alphabet = 0; + } + */ + + //printf("%d %s: %.0f%%\n", i, names[class], prob*100); int offset = class*123457 % classes; float red = get_color(2,offset,classes); float green = get_color(1,offset,classes); @@ -205,7 +277,8 @@ void draw_detections(image im, int num, float thresh, box *boxes, float **probs, rgb[0] = red; rgb[1] = green; rgb[2] = blue; - box b = boxes[i]; + box b = dets[i].bbox; + //printf("%f %f %f %f\n", b.x, b.y, b.w, b.h); int left = (b.x-b.w/2.)*im.w; int right = (b.x+b.w/2.)*im.w; @@ -219,8 +292,18 @@ void draw_detections(image im, int num, float thresh, box *boxes, float **probs, draw_box_width(im, left, top, right, bot, width, red, green, blue); if (alphabet) { - image label = get_label(alphabet, names[class], (im.h*.03)/10); + image label = get_label(alphabet, labelstr, (im.h*.03)); draw_label(im, top + width, left, label, rgb); + free_image(label); + } + if (dets[i].mask){ + image mask = float_to_image(14, 14, 1, dets[i].mask); + image resized_mask = resize_image(mask, b.w*im.w, b.h*im.h); + image tmask = threshold_image(resized_mask, .5); + embed_image(tmask, im, left, top); + free_image(mask); + free_image(resized_mask); + free_image(tmask); } } } @@ -294,6 +377,54 @@ image image_distance(image a, image b) return dist; } +void ghost_image(image source, image dest, int dx, int dy) +{ + int x,y,k; + float max_dist = sqrt((-source.w/2. + .5)*(-source.w/2. + .5)); + for(k = 0; k < source.c; ++k){ + for(y = 0; y < source.h; ++y){ + for(x = 0; x < source.w; ++x){ + float dist = sqrt((x - source.w/2. + .5)*(x - source.w/2. + .5) + (y - source.h/2. + .5)*(y - source.h/2. + .5)); + float alpha = (1 - dist/max_dist); + if(alpha < 0) alpha = 0; + float v1 = get_pixel(source, x,y,k); + float v2 = get_pixel(dest, dx+x,dy+y,k); + float val = alpha*v1 + (1-alpha)*v2; + set_pixel(dest, dx+x, dy+y, k, val); + } + } + } +} + +void blocky_image(image im, int s) +{ + int i,j,k; + for(k = 0; k < im.c; ++k){ + for(j = 0; j < im.h; ++j){ + for(i = 0; i < im.w; ++i){ + im.data[i + im.w*(j + im.h*k)] = im.data[i/s*s + im.w*(j/s*s + im.h*k)]; + } + } + } +} + +void censor_image(image im, int dx, int dy, int w, int h) +{ + int i,j,k; + int s = 32; + if(dx < 0) dx = 0; + if(dy < 0) dy = 0; + + for(k = 0; k < im.c; ++k){ + for(j = dy; j < dy + h && j < im.h; ++j){ + for(i = dx; i < dx + w && i < im.w; ++i){ + im.data[i + im.w*(j + im.h*k)] = im.data[i/s*s + im.w*(j/s*s + im.h*k)]; + //im.data[i + j*im.w + k*im.w*im.h] = 0; + } + } + } +} + void embed_image(image source, image dest, int dx, int dy) { int x,y,k; @@ -380,6 +511,11 @@ void normalize_image2(image p) free(max); } +void copy_image_into(image src, image dest) +{ + memcpy(dest.data, src.data, src.h*src.w*src.c*sizeof(float)); +} + image copy_image(image p) { image copy = p; @@ -398,145 +534,27 @@ void rgbgr_image(image im) } } -#ifdef OPENCV -void show_image_cv(image p, const char *name) -{ - int x,y,k; - image copy = copy_image(p); - constrain_image(copy); - if(p.c == 3) rgbgr_image(copy); - //normalize_image(copy); - - char buff[256]; - //sprintf(buff, "%s (%d)", name, windows); - sprintf(buff, "%s", name); - - IplImage *disp = cvCreateImage(cvSize(p.w,p.h), IPL_DEPTH_8U, p.c); - int step = disp->widthStep; - cvNamedWindow(buff, CV_WINDOW_NORMAL); - //cvMoveWindow(buff, 100*(windows%10) + 200*(windows/10), 100*(windows%10)); - ++windows; - for(y = 0; y < p.h; ++y){ - for(x = 0; x < p.w; ++x){ - for(k= 0; k < p.c; ++k){ - disp->imageData[y*step + x*p.c + k] = (unsigned char)(get_pixel(copy,x,y,k)*255); - } - } - } - free_image(copy); - if(0){ - int w = 448; - int h = w*p.h/p.w; - if(h > 1000){ - h = 1000; - w = h*p.w/p.h; - } - IplImage *buffer = disp; - disp = cvCreateImage(cvSize(w, h), buffer->depth, buffer->nChannels); - cvResize(buffer, disp, CV_INTER_LINEAR); - cvReleaseImage(&buffer); - } - cvShowImage(buff, disp); - cvReleaseImage(&disp); -} -#endif - -void show_image(image p, const char *name) +int show_image(image p, const char *name, int ms) { #ifdef OPENCV - show_image_cv(p, name); + int c = show_image_cv(p, name, ms); + return c; #else fprintf(stderr, "Not compiled with OpenCV, saving to %s.png instead\n", name); save_image(p, name); + return -1; #endif } -#ifdef OPENCV - -image ipl_to_image(IplImage* src) -{ - unsigned char *data = (unsigned char *)src->imageData; - int h = src->height; - int w = src->width; - int c = src->nChannels; - int step = src->widthStep; - image out = make_image(w, h, c); - int i, j, k, count=0;; - - for(k= 0; k < c; ++k){ - for(i = 0; i < h; ++i){ - for(j = 0; j < w; ++j){ - out.data[count++] = data[i*step + j*c + k]/255.; - } - } - } - return out; -} - -image load_image_cv(char *filename, int channels) -{ - IplImage* src = 0; - int flag = -1; - if (channels == 0) flag = -1; - else if (channels == 1) flag = 0; - else if (channels == 3) flag = 1; - else { - fprintf(stderr, "OpenCV can't force load with %d channels\n", channels); - } - - if( (src = cvLoadImage(filename, flag)) == 0 ) - { - fprintf(stderr, "Cannot load image \"%s\"\n", filename); - char buff[256]; - sprintf(buff, "echo %s >> bad.list", filename); - system(buff); - return make_image(10,10,3); - //exit(0); - } - image out = ipl_to_image(src); - cvReleaseImage(&src); - rgbgr_image(out); - return out; -} - -image get_image_from_stream(CvCapture *cap) -{ - IplImage* src = cvQueryFrame(cap); - if (!src) return make_empty_image(0,0,0); - image im = ipl_to_image(src); - rgbgr_image(im); - return im; -} - -void save_image_jpg(image p, const char *name) -{ - image copy = copy_image(p); - if(p.c == 3) rgbgr_image(copy); - int x,y,k; - - char buff[256]; - sprintf(buff, "%s.jpg", name); - - IplImage *disp = cvCreateImage(cvSize(p.w,p.h), IPL_DEPTH_8U, p.c); - int step = disp->widthStep; - for(y = 0; y < p.h; ++y){ - for(x = 0; x < p.w; ++x){ - for(k= 0; k < p.c; ++k){ - disp->imageData[y*step + x*p.c + k] = (unsigned char)(get_pixel(copy,x,y,k)*255); - } - } - } - cvSaveImage(buff, disp,0); - cvReleaseImage(&disp); - free_image(copy); -} -#endif - -void save_image_png(image im, const char *name) +void save_image_options(image im, const char *name, IMTYPE f, int quality) { char buff[256]; //sprintf(buff, "%s (%d)", name, windows); - sprintf(buff, "%s.png", name); + if(f == PNG) sprintf(buff, "%s.png", name); + else if (f == BMP) sprintf(buff, "%s.bmp", name); + else if (f == TGA) sprintf(buff, "%s.tga", name); + else if (f == JPG) sprintf(buff, "%s.jpg", name); + else sprintf(buff, "%s.png", name); unsigned char *data = calloc(im.w*im.h*im.c, sizeof(char)); int i,k; for(k = 0; k < im.c; ++k){ @@ -544,21 +562,20 @@ void save_image_png(image im, const char *name) data[i*im.c+k] = (unsigned char) (255*im.data[i + k*im.w*im.h]); } } - int success = stbi_write_png(buff, im.w, im.h, im.c, data, im.w*im.c); + int success = 0; + if(f == PNG) success = stbi_write_png(buff, im.w, im.h, im.c, data, im.w*im.c); + else if (f == BMP) success = stbi_write_bmp(buff, im.w, im.h, im.c, data); + else if (f == TGA) success = stbi_write_tga(buff, im.w, im.h, im.c, data); + else if (f == JPG) success = stbi_write_jpg(buff, im.w, im.h, im.c, data, quality); free(data); if(!success) fprintf(stderr, "Failed to write image %s\n", buff); } void save_image(image im, const char *name) { -#ifdef OPENCV - save_image_jpg(im, name); -#else - save_image_png(im, name); -#endif + save_image_options(im, name, JPG, 80); } - void show_image_layers(image p, char *name) { int i; @@ -566,7 +583,7 @@ void show_image_layers(image p, char *name) for(i = 0; i < p.c; ++i){ sprintf(buff, "%s - Layer %d", name, i); image layer = get_image_layer(p, i); - show_image(layer, buff); + show_image(layer, buff, 1); free_image(layer); } } @@ -574,7 +591,7 @@ void show_image_layers(image p, char *name) void show_image_collapsed(image p, char *name) { image c = collapse_image_layers(p, 1); - show_image(c, name); + show_image(c, name, 1); free_image(c); } @@ -613,6 +630,29 @@ image float_to_image(int w, int h, int c, float *data) return out; } +void place_image(image im, int w, int h, int dx, int dy, image canvas) +{ + int x, y, c; + for(c = 0; c < im.c; ++c){ + for(y = 0; y < h; ++y){ + for(x = 0; x < w; ++x){ + float rx = ((float)x / w) * im.w; + float ry = ((float)y / h) * im.h; + float val = bilinear_interpolate(im, rx, ry, c); + set_pixel(canvas, x + dx, y + dy, c, val); + } + } + } +} + +image center_crop_image(image im, int w, int h) +{ + int m = (im.w < im.h) ? im.w : im.h; + image c = crop_image(im, (im.w - m) / 2, (im.h - m)/2, m, m); + image r = resize_image(c, w, h); + free_image(c); + return r; +} image rotate_crop_image(image im, float rad, float s, int w, int h, float dx, float dy, float aspect) { @@ -652,6 +692,12 @@ image rotate_image(image im, float rad) return rot; } +void fill_image(image m, float s) +{ + int i; + for(i = 0; i < m.h*m.w*m.c; ++i) m.data[i] = s; +} + void translate_image(image m, float s) { int i; @@ -676,9 +722,7 @@ image crop_image(image im, int dx, int dy, int w, int h) float val = 0; r = constrain_int(r, 0, im.h-1); c = constrain_int(c, 0, im.w-1); - if (r >= 0 && r < im.h && c >= 0 && c < im.w) { - val = get_pixel(im, c, r, k); - } + val = get_pixel(im, c, r, k); set_pixel(cropped, i, j, k, val); } } @@ -746,11 +790,44 @@ void composite_3d(char *f1, char *f2, char *out, int delta) for(i = 0; i < c.w*c.h; ++i){ c.data[i] = a.data[i]; } -#ifdef OPENCV - save_image_jpg(c, out); -#else save_image(c, out); -#endif +} + +void letterbox_image_into(image im, int w, int h, image boxed) +{ + int new_w = im.w; + int new_h = im.h; + if (((float)w/im.w) < ((float)h/im.h)) { + new_w = w; + new_h = (im.h * w)/im.w; + } else { + new_h = h; + new_w = (im.w * h)/im.h; + } + image resized = resize_image(im, new_w, new_h); + embed_image(resized, boxed, (w-new_w)/2, (h-new_h)/2); + free_image(resized); +} + +image letterbox_image(image im, int w, int h) +{ + int new_w = im.w; + int new_h = im.h; + if (((float)w/im.w) < ((float)h/im.h)) { + new_w = w; + new_h = (im.h * w)/im.w; + } else { + new_h = h; + new_w = (im.w * h)/im.h; + } + image resized = resize_image(im, new_w, new_h); + image boxed = make_image(w, h, im.c); + fill_image(boxed, .5); + //int i; + //for(i = 0; i < boxed.w*boxed.h*boxed.c; ++i) boxed.data[i] = 0; + embed_image(resized, boxed, (w-new_w)/2, (h-new_h)/2); + free_image(resized); + return boxed; } image resize_max(image im, int max) @@ -793,8 +870,9 @@ image random_crop_image(image im, int w, int h) return crop; } -image random_augment_image(image im, float angle, float aspect, int low, int high, int size) +augment_args random_augment_args(image im, float angle, float aspect, int low, int high, int w, int h) { + augment_args a = {0}; aspect = rand_scale(aspect); int r = rand_int(low, high); int min = (im.h < im.w*aspect) ? im.h : im.w*aspect; @@ -802,15 +880,27 @@ image random_augment_image(image im, float angle, float aspect, int low, int hig float rad = rand_uniform(-angle, angle) * TWO_PI / 360.; - float dx = (im.w*scale/aspect - size) / 2.; - float dy = (im.h*scale - size) / 2.; - if(dx < 0) dx = 0; - if(dy < 0) dy = 0; + float dx = (im.w*scale/aspect - w) / 2.; + float dy = (im.h*scale - w) / 2.; + //if(dx < 0) dx = 0; + //if(dy < 0) dy = 0; dx = rand_uniform(-dx, dx); dy = rand_uniform(-dy, dy); - image crop = rotate_crop_image(im, rad, scale, size, size, dx, dy, aspect); + a.rad = rad; + a.scale = scale; + a.w = w; + a.h = h; + a.dx = dx; + a.dy = dy; + a.aspect = aspect; + return a; +} +image random_augment_image(image im, float angle, float aspect, int low, int high, int w, int h) +{ + augment_args a = random_augment_args(im, angle, aspect, low, high, w, h); + image crop = rotate_crop_image(im, a.rad, a.scale, a.w, a.h, a.dx, a.dy, a.aspect); return crop; } @@ -824,6 +914,52 @@ float three_way_min(float a, float b, float c) return (a < b) ? ( (a < c) ? a : c) : ( (b < c) ? b : c) ; } +void yuv_to_rgb(image im) +{ + assert(im.c == 3); + int i, j; + float r, g, b; + float y, u, v; + for(j = 0; j < im.h; ++j){ + for(i = 0; i < im.w; ++i){ + y = get_pixel(im, i , j, 0); + u = get_pixel(im, i , j, 1); + v = get_pixel(im, i , j, 2); + + r = y + 1.13983*v; + g = y + -.39465*u + -.58060*v; + b = y + 2.03211*u; + + set_pixel(im, i, j, 0, r); + set_pixel(im, i, j, 1, g); + set_pixel(im, i, j, 2, b); + } + } +} + +void rgb_to_yuv(image im) +{ + assert(im.c == 3); + int i, j; + float r, g, b; + float y, u, v; + for(j = 0; j < im.h; ++j){ + for(i = 0; i < im.w; ++i){ + r = get_pixel(im, i , j, 0); + g = get_pixel(im, i , j, 1); + b = get_pixel(im, i , j, 2); + + y = .299*r + .587*g + .114*b; + u = -.14713*r + -.28886*g + .436*b; + v = .615*r + -.51499*g + -.10001*b; + + set_pixel(im, i, j, 0, y); + set_pixel(im, i, j, 1, u); + set_pixel(im, i, j, 2, v); + } + } +} + // http://www.cs.rit.edu/~ncs/color/t_convert.html void rgb_to_hsv(image im) { @@ -903,12 +1039,30 @@ void hsv_to_rgb(image im) } } +void grayscale_image_3c(image im) +{ + assert(im.c == 3); + int i, j, k; + float scale[] = {0.299, 0.587, 0.114}; + for(j = 0; j < im.h; ++j){ + for(i = 0; i < im.w; ++i){ + float val = 0; + for(k = 0; k < 3; ++k){ + val += scale[k]*get_pixel(im, i, j, k); + } + im.data[0*im.h*im.w + im.w*j + i] = val; + im.data[1*im.h*im.w + im.w*j + i] = val; + im.data[2*im.h*im.w + im.w*j + i] = val; + } + } +} + image grayscale_image(image im) { assert(im.c == 3); int i, j, k; image gray = make_image(im.w, im.h, 1); - float scale[] = {0.587, 0.299, 0.114}; + float scale[] = {0.299, 0.587, 0.114}; for(k = 0; k < im.c; ++k){ for(j = 0; j < im.h; ++j){ for(i = 0; i < im.w; ++i){ @@ -1042,21 +1196,6 @@ void saturate_exposure_image(image im, float sat, float exposure) constrain_image(im); } -float bilinear_interpolate(image im, float x, float y, int c) -{ - int ix = (int) floorf(x); - int iy = (int) floorf(y); - - float dx = x - ix; - float dy = y - iy; - - float val = (1-dy) * (1-dx) * get_pixel_extend(im, ix, iy, c) + - dy * (1-dx) * get_pixel_extend(im, ix, iy+1, c) + - (1-dy) * dx * get_pixel_extend(im, ix+1, iy, c) + - dy * dx * get_pixel_extend(im, ix+1, iy+1, c); - return val; -} - image resize_image(image im, int w, int h) { image resized = make_image(w, h, im.c); @@ -1119,16 +1258,16 @@ void test_resize(char *filename) distort_image(c4, .1, .66666, 1.5); - show_image(im, "Original"); - show_image(gray, "Gray"); - show_image(c1, "C1"); - show_image(c2, "C2"); - show_image(c3, "C3"); - show_image(c4, "C4"); + show_image(im, "Original", 1); + show_image(gray, "Gray", 1); + show_image(c1, "C1", 1); + show_image(c2, "C2", 1); + show_image(c3, "C3", 1); + show_image(c4, "C4", 1); #ifdef OPENCV while(1){ - image aug = random_augment_image(im, 0, .75, 320, 448, 320); - show_image(aug, "aug"); + image aug = random_augment_image(im, 0, .75, 320, 448, 320, 320); + show_image(aug, "aug", 1); free_image(aug); @@ -1143,10 +1282,9 @@ void test_resize(char *filename) float dhue = rand_uniform(-hue, hue); distort_image(c, dhue, dsat, dexp); - show_image(c, "rand"); + show_image(c, "rand", 1); printf("%f %f %f\n", dhue, dsat, dexp); free_image(c); - cvWaitKey(0); } #endif } @@ -1206,33 +1344,6 @@ image get_image_layer(image m, int l) } return out; } - -float get_pixel(image m, int x, int y, int c) -{ - assert(x < m.w && y < m.h && c < m.c); - return m.data[c*m.h*m.w + y*m.w + x]; -} -float get_pixel_extend(image m, int x, int y, int c) -{ - if(x < 0) x = 0; - if(x >= m.w) x = m.w-1; - if(y < 0) y = 0; - if(y >= m.h) y = m.h-1; - if(c < 0 || c >= m.c) return 0; - return get_pixel(m, x, y, c); -} -void set_pixel(image m, int x, int y, int c, float val) -{ - if (x < 0 || y < 0 || c < 0 || x >= m.w || y >= m.h || c >= m.c) return; - assert(x < m.w && y < m.h && c < m.c); - m.data[c*m.h*m.w + y*m.w + x] = val; -} -void add_pixel(image m, int x, int y, int c, float val) -{ - assert(x < m.w && y < m.h && c < m.c); - m.data[c*m.h*m.w + y*m.w + x] += val; -} - void print_image(image m) { int i, j, k; @@ -1325,7 +1436,7 @@ void show_image_normalized(image im, const char *name) { image c = copy_image(im); normalize_image(c); - show_image(c, name); + show_image(c, name, 1); free_image(c); } @@ -1343,7 +1454,7 @@ void show_images(image *ims, int n, char *window) */ normalize_image(m); save_image(m, window); - show_image(m, window); + show_image(m, window, 1); free_image(m); } diff --git a/image.darknet/src/image.h b/image.darknet/src/image.h index 39c3962..3392bb9 100644 --- a/image.darknet/src/image.h +++ b/image.darknet/src/image.h @@ -7,81 +7,63 @@ #include #include #include "box.h" +#include "darknet.h" -typedef struct { - int h; - int w; - int c; - float *data; -} image; +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef OPENCV +void *open_video_stream(const char *f, int c, int w, int h, int fps); +image get_image_from_stream(void *p); +image load_image_cv(char *filename, int channels); +int show_image_cv(image im, const char* name, int ms); +#endif float get_color(int c, int x, int max); -void flip_image(image a); void draw_box(image a, int x1, int y1, int x2, int y2, float r, float g, float b); -void draw_box_width(image a, int x1, int y1, int x2, int y2, int w, float r, float g, float b); void draw_bbox(image a, box bbox, int w, float r, float g, float b); -void draw_label(image a, int r, int c, image label, const float *rgb); void write_label(image a, int r, int c, image *characters, char *string, float *rgb); -void draw_detections(image im, int num, float thresh, box *boxes, float **probs, char **names, image **labels, int classes); image image_distance(image a, image b); void scale_image(image m, float s); -image crop_image(image im, int dx, int dy, int w, int h); +image rotate_crop_image(image im, float rad, float s, int w, int h, float dx, float dy, float aspect); image random_crop_image(image im, int w, int h); -image random_augment_image(image im, float angle, float aspect, int low, int high, int size); -void random_distort_image(image im, float hue, float saturation, float exposure); -image resize_image(image im, int w, int h); -image resize_min(image im, int min); +image random_augment_image(image im, float angle, float aspect, int low, int high, int w, int h); +augment_args random_augment_args(image im, float angle, float aspect, int low, int high, int w, int h); +void letterbox_image_into(image im, int w, int h, image boxed); image resize_max(image im, int max); void translate_image(image m, float s); -void normalize_image(image p); -image rotate_image(image m, float rad); -void rotate_image_cw(image im, int times); void embed_image(image source, image dest, int dx, int dy); +void place_image(image im, int w, int h, int dx, int dy, image canvas); void saturate_image(image im, float sat); void exposure_image(image im, float sat); void distort_image(image im, float hue, float sat, float val); void saturate_exposure_image(image im, float sat, float exposure); +void rgb_to_hsv(image im); void hsv_to_rgb(image im); -void rgbgr_image(image im); -void constrain_image(image im); -void composite_3d(char *f1, char *f2, char *out, int delta); -int best_3d_shift_r(image a, image b, int min, int max); +void yuv_to_rgb(image im); +void rgb_to_yuv(image im); -image grayscale_image(image im); -image threshold_image(image im, float thresh); image collapse_image_layers(image source, int border); image collapse_images_horz(image *ims, int n); image collapse_images_vert(image *ims, int n); -void show_image(image p, const char *name); void show_image_normalized(image im, const char *name); -void save_image_png(image im, const char *name); -void save_image(image p, const char *name); void show_images(image *ims, int n, char *window); void show_image_layers(image p, char *name); void show_image_collapsed(image p, char *name); void print_image(image m); -image make_image(int w, int h, int c); -image make_random_image(int w, int h, int c); image make_empty_image(int w, int h, int c); -image float_to_image(int w, int h, int c, float *data); -image copy_image(image p); -image load_image(char *filename, int w, int h, int c); -image load_image_color(char *filename, int w, int h); -image **load_alphabet(); - -float get_pixel(image m, int x, int y, int c); -float get_pixel_extend(image m, int x, int y, int c); -void set_pixel(image m, int x, int y, int c, float val); -void add_pixel(image m, int x, int y, int c, float val); -float bilinear_interpolate(image im, float x, float y, int c); +void copy_image_into(image src, image dest); image get_image_layer(image m, int l); -void free_image(image m); -void test_resize(char *filename); +#ifdef __cplusplus +} +#endif + #endif diff --git a/image.darknet/src/image_opencv.cpp b/image.darknet/src/image_opencv.cpp new file mode 100644 index 0000000..7511280 --- /dev/null +++ b/image.darknet/src/image_opencv.cpp @@ -0,0 +1,135 @@ +#ifdef OPENCV + +#include "stdio.h" +#include "stdlib.h" +#include "opencv2/opencv.hpp" +#include "image.h" + +using namespace cv; + +extern "C" { + +IplImage *image_to_ipl(image im) +{ + int x,y,c; + IplImage *disp = cvCreateImage(cvSize(im.w,im.h), IPL_DEPTH_8U, im.c); + int step = disp->widthStep; + for(y = 0; y < im.h; ++y){ + for(x = 0; x < im.w; ++x){ + for(c= 0; c < im.c; ++c){ + float val = im.data[c*im.h*im.w + y*im.w + x]; + disp->imageData[y*step + x*im.c + c] = (unsigned char)(val*255); + } + } + } + return disp; +} + +image ipl_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; +} + +Mat image_to_mat(image im) +{ + image copy = copy_image(im); + constrain_image(copy); + if(im.c == 3) rgbgr_image(copy); + + IplImage *ipl = image_to_ipl(copy); + Mat m = cvarrToMat(ipl, true); + cvReleaseImage(&ipl); + free_image(copy); + return m; +} + +image mat_to_image(Mat m) +{ + IplImage ipl = m; + image im = ipl_to_image(&ipl); + rgbgr_image(im); + return im; +} + +void *open_video_stream(const char *f, int c, int w, int h, int fps) +{ + VideoCapture *cap; + if(f) cap = new VideoCapture(f); + else cap = new VideoCapture(c); + if(!cap->isOpened()) return 0; + if(w) cap->set(CV_CAP_PROP_FRAME_WIDTH, w); + if(h) cap->set(CV_CAP_PROP_FRAME_HEIGHT, w); + if(fps) cap->set(CV_CAP_PROP_FPS, w); + return (void *) cap; +} + +image get_image_from_stream(void *p) +{ + VideoCapture *cap = (VideoCapture *)p; + Mat m; + *cap >> m; + if(m.empty()) return make_empty_image(0,0,0); + return mat_to_image(m); +} + +image load_image_cv(char *filename, int channels) +{ + int flag = -1; + if (channels == 0) flag = -1; + else if (channels == 1) flag = 0; + else if (channels == 3) flag = 1; + else { + fprintf(stderr, "OpenCV can't force load with %d channels\n", channels); + } + Mat m; + m = imread(filename, flag); + if(!m.data){ + fprintf(stderr, "Cannot load image \"%s\"\n", filename); + char buff[256]; + sprintf(buff, "echo %s >> bad.list", filename); + system(buff); + return make_image(10,10,3); + //exit(0); + } + image im = mat_to_image(m); + return im; +} + +int show_image_cv(image im, const char* name, int ms) +{ + Mat m = image_to_mat(im); + imshow(name, m); + int c = waitKey(ms); + if (c != -1) c = c%256; + return c; +} + +void make_window(char *name, int w, int h, int fullscreen) +{ + namedWindow(name, WINDOW_NORMAL); + if (fullscreen) { + setWindowProperty(name, CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN); + } else { + resizeWindow(name, w, h); + if(strcmp(name, "Demo") == 0) moveWindow(name, 0, 0); + } +} + +} + +#endif diff --git a/image.darknet/src/iseg_layer.c b/image.darknet/src/iseg_layer.c new file mode 100644 index 0000000..2bf03a8 --- /dev/null +++ b/image.darknet/src/iseg_layer.c @@ -0,0 +1,225 @@ +#include "iseg_layer.h" +#include "activations.h" +#include "blas.h" +#include "box.h" +#include "cuda.h" +#include "utils.h" + +#include +#include +#include +#include + +layer make_iseg_layer(int batch, int w, int h, int classes, int ids) +{ + layer l = {0}; + l.type = ISEG; + + l.h = h; + l.w = w; + l.c = classes + ids; + l.out_w = l.w; + l.out_h = l.h; + l.out_c = l.c; + l.classes = classes; + l.batch = batch; + l.extra = ids; + l.cost = calloc(1, sizeof(float)); + l.outputs = h*w*l.c; + l.inputs = l.outputs; + l.truths = 90*(l.w*l.h+1); + l.delta = calloc(batch*l.outputs, sizeof(float)); + l.output = calloc(batch*l.outputs, sizeof(float)); + + l.counts = calloc(90, sizeof(int)); + l.sums = calloc(90, sizeof(float*)); + if(ids){ + int i; + for(i = 0; i < 90; ++i){ + l.sums[i] = calloc(ids, sizeof(float)); + } + } + + l.forward = forward_iseg_layer; + l.backward = backward_iseg_layer; +#ifdef GPU + l.forward_gpu = forward_iseg_layer_gpu; + l.backward_gpu = backward_iseg_layer_gpu; + l.output_gpu = cuda_make_array(l.output, batch*l.outputs); + l.delta_gpu = cuda_make_array(l.delta, batch*l.outputs); +#endif + + fprintf(stderr, "iseg\n"); + srand(0); + + return l; +} + +void resize_iseg_layer(layer *l, int w, int h) +{ + l->w = w; + l->h = h; + + l->outputs = h*w*l->c; + l->inputs = l->outputs; + + l->output = realloc(l->output, l->batch*l->outputs*sizeof(float)); + l->delta = realloc(l->delta, l->batch*l->outputs*sizeof(float)); + +#ifdef GPU + cuda_free(l->delta_gpu); + cuda_free(l->output_gpu); + + l->delta_gpu = cuda_make_array(l->delta, l->batch*l->outputs); + l->output_gpu = cuda_make_array(l->output, l->batch*l->outputs); +#endif +} + +void forward_iseg_layer(const layer l, network net) +{ + + double time = what_time_is_it_now(); + int i,b,j,k; + int ids = l.extra; + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); + memset(l.delta, 0, l.outputs * l.batch * sizeof(float)); + +#ifndef GPU + for (b = 0; b < l.batch; ++b){ + int index = b*l.outputs; + activate_array(l.output + index, l.classes*l.w*l.h, LOGISTIC); + } +#endif + + for (b = 0; b < l.batch; ++b){ + // a priori, each pixel has no class + for(i = 0; i < l.classes; ++i){ + for(k = 0; k < l.w*l.h; ++k){ + int index = b*l.outputs + i*l.w*l.h + k; + l.delta[index] = 0 - l.output[index]; + } + } + + // a priori, embedding should be small magnitude + for(i = 0; i < ids; ++i){ + for(k = 0; k < l.w*l.h; ++k){ + int index = b*l.outputs + (i+l.classes)*l.w*l.h + k; + l.delta[index] = .1 * (0 - l.output[index]); + } + } + + + memset(l.counts, 0, 90*sizeof(int)); + for(i = 0; i < 90; ++i){ + fill_cpu(ids, 0, l.sums[i], 1); + + int c = net.truth[b*l.truths + i*(l.w*l.h+1)]; + if(c < 0) break; + // add up metric embeddings for each instance + for(k = 0; k < l.w*l.h; ++k){ + int index = b*l.outputs + c*l.w*l.h + k; + float v = net.truth[b*l.truths + i*(l.w*l.h + 1) + 1 + k]; + if(v){ + l.delta[index] = v - l.output[index]; + axpy_cpu(ids, 1, l.output + b*l.outputs + l.classes*l.w*l.h + k, l.w*l.h, l.sums[i], 1); + ++l.counts[i]; + } + } + } + + float *mse = calloc(90, sizeof(float)); + for(i = 0; i < 90; ++i){ + int c = net.truth[b*l.truths + i*(l.w*l.h+1)]; + if(c < 0) break; + for(k = 0; k < l.w*l.h; ++k){ + float v = net.truth[b*l.truths + i*(l.w*l.h + 1) + 1 + k]; + if(v){ + int z; + float sum = 0; + for(z = 0; z < ids; ++z){ + int index = b*l.outputs + (l.classes + z)*l.w*l.h + k; + sum += pow(l.sums[i][z]/l.counts[i] - l.output[index], 2); + } + mse[i] += sum; + } + } + mse[i] /= l.counts[i]; + } + + // Calculate average embedding + for(i = 0; i < 90; ++i){ + if(!l.counts[i]) continue; + scal_cpu(ids, 1.f/l.counts[i], l.sums[i], 1); + if(b == 0 && net.gpu_index == 0){ + printf("%4d, %6.3f, ", l.counts[i], mse[i]); + for(j = 0; j < ids; ++j){ + printf("%6.3f,", l.sums[i][j]); + } + printf("\n"); + } + } + free(mse); + + // Calculate embedding loss + for(i = 0; i < 90; ++i){ + if(!l.counts[i]) continue; + for(k = 0; k < l.w*l.h; ++k){ + float v = net.truth[b*l.truths + i*(l.w*l.h + 1) + 1 + k]; + if(v){ + for(j = 0; j < 90; ++j){ + if(!l.counts[j])continue; + int z; + for(z = 0; z < ids; ++z){ + int index = b*l.outputs + (l.classes + z)*l.w*l.h + k; + float diff = l.sums[j][z] - l.output[index]; + if (j == i) l.delta[index] += diff < 0? -.1 : .1; + else l.delta[index] += -(diff < 0? -.1 : .1); + } + } + } + } + } + + for(i = 0; i < ids; ++i){ + for(k = 0; k < l.w*l.h; ++k){ + int index = b*l.outputs + (i+l.classes)*l.w*l.h + k; + l.delta[index] *= .01; + } + } + } + + *(l.cost) = pow(mag_array(l.delta, l.outputs * l.batch), 2); + printf("took %lf sec\n", what_time_is_it_now() - time); +} + +void backward_iseg_layer(const layer l, network net) +{ + axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, net.delta, 1); +} + +#ifdef GPU + +void forward_iseg_layer_gpu(const layer l, network net) +{ + copy_gpu(l.batch*l.inputs, net.input_gpu, 1, l.output_gpu, 1); + int b; + for (b = 0; b < l.batch; ++b){ + activate_array_gpu(l.output_gpu + b*l.outputs, l.classes*l.w*l.h, LOGISTIC); + //if(l.extra) activate_array_gpu(l.output_gpu + b*l.outputs + l.classes*l.w*l.h, l.extra*l.w*l.h, LOGISTIC); + } + + cuda_pull_array(l.output_gpu, net.input, l.batch*l.inputs); + forward_iseg_layer(l, net); + cuda_push_array(l.delta_gpu, l.delta, l.batch*l.outputs); +} + +void backward_iseg_layer_gpu(const layer l, network net) +{ + int b; + for (b = 0; b < l.batch; ++b){ + //if(l.extra) gradient_array_gpu(l.output_gpu + b*l.outputs + l.classes*l.w*l.h, l.extra*l.w*l.h, LOGISTIC, l.delta_gpu + b*l.outputs + l.classes*l.w*l.h); + } + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); +} +#endif + diff --git a/image.darknet/src/iseg_layer.h b/image.darknet/src/iseg_layer.h new file mode 100644 index 0000000..dd8e64e --- /dev/null +++ b/image.darknet/src/iseg_layer.h @@ -0,0 +1,19 @@ +#ifndef ISEG_LAYER_H +#define ISEG_LAYER_H + +#include "darknet.h" +#include "layer.h" +#include "network.h" + +layer make_iseg_layer(int batch, int w, int h, int classes, int ids); +void forward_iseg_layer(const layer l, network net); +void backward_iseg_layer(const layer l, network net); +void resize_iseg_layer(layer *l, int w, int h); +int iseg_num_detections(layer l, float thresh); + +#ifdef GPU +void forward_iseg_layer_gpu(const layer l, network net); +void backward_iseg_layer_gpu(layer l, network net); +#endif + +#endif diff --git a/image.darknet/src/l2norm_layer.c b/image.darknet/src/l2norm_layer.c new file mode 100644 index 0000000..d099479 --- /dev/null +++ b/image.darknet/src/l2norm_layer.c @@ -0,0 +1,63 @@ +#include "l2norm_layer.h" +#include "activations.h" +#include "blas.h" +#include "cuda.h" + +#include +#include +#include +#include +#include + +layer make_l2norm_layer(int batch, int inputs) +{ + fprintf(stderr, "l2norm %4d\n", inputs); + layer l = {0}; + l.type = L2NORM; + l.batch = batch; + l.inputs = inputs; + l.outputs = inputs; + l.output = calloc(inputs*batch, sizeof(float)); + l.scales = calloc(inputs*batch, sizeof(float)); + l.delta = calloc(inputs*batch, sizeof(float)); + + l.forward = forward_l2norm_layer; + l.backward = backward_l2norm_layer; + #ifdef GPU + l.forward_gpu = forward_l2norm_layer_gpu; + l.backward_gpu = backward_l2norm_layer_gpu; + + l.output_gpu = cuda_make_array(l.output, inputs*batch); + l.scales_gpu = cuda_make_array(l.output, inputs*batch); + l.delta_gpu = cuda_make_array(l.delta, inputs*batch); + #endif + return l; +} + +void forward_l2norm_layer(const layer l, network net) +{ + copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); + l2normalize_cpu(l.output, l.scales, l.batch, l.out_c, l.out_w*l.out_h); +} + +void backward_l2norm_layer(const layer l, network net) +{ + //axpy_cpu(l.inputs*l.batch, 1, l.scales, 1, l.delta, 1); + axpy_cpu(l.inputs*l.batch, 1, l.delta, 1, net.delta, 1); +} + +#ifdef GPU + +void forward_l2norm_layer_gpu(const layer l, network net) +{ + copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + l2normalize_gpu(l.output_gpu, l.scales_gpu, l.batch, l.out_c, l.out_w*l.out_h); +} + +void backward_l2norm_layer_gpu(const layer l, network net) +{ + axpy_gpu(l.batch*l.inputs, 1, l.scales_gpu, 1, l.delta_gpu, 1); + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); +} + +#endif diff --git a/image.darknet/src/l2norm_layer.h b/image.darknet/src/l2norm_layer.h new file mode 100644 index 0000000..1ca6f71 --- /dev/null +++ b/image.darknet/src/l2norm_layer.h @@ -0,0 +1,15 @@ +#ifndef L2NORM_LAYER_H +#define L2NORM_LAYER_H +#include "layer.h" +#include "network.h" + +layer make_l2norm_layer(int batch, int inputs); +void forward_l2norm_layer(const layer l, network net); +void backward_l2norm_layer(const layer l, network net); + +#ifdef GPU +void forward_l2norm_layer_gpu(const layer l, network net); +void backward_l2norm_layer_gpu(const layer l, network net); +#endif + +#endif diff --git a/image.darknet/src/layer.c b/image.darknet/src/layer.c index 622cf26..c27b477 100644 --- a/image.darknet/src/layer.c +++ b/image.darknet/src/layer.c @@ -1,5 +1,6 @@ #include "layer.h" #include "cuda.h" + #include void free_layer(layer l) @@ -32,7 +33,6 @@ void free_layer(layer l) if(l.scale_updates) free(l.scale_updates); if(l.weights) free(l.weights); if(l.weight_updates) free(l.weight_updates); - if(l.col_image) free(l.col_image); if(l.delta) free(l.delta); if(l.output) free(l.output); if(l.squared) free(l.squared); @@ -80,7 +80,6 @@ void free_layer(layer l) if(l.rolling_variance_gpu) cuda_free(l.rolling_variance_gpu); if(l.variance_delta_gpu) cuda_free(l.variance_delta_gpu); if(l.mean_delta_gpu) cuda_free(l.mean_delta_gpu); - if(l.col_image_gpu) cuda_free(l.col_image_gpu); if(l.x_gpu) cuda_free(l.x_gpu); if(l.x_norm_gpu) cuda_free(l.x_norm_gpu); if(l.weights_gpu) cuda_free(l.weights_gpu); diff --git a/image.darknet/src/layer.h b/image.darknet/src/layer.h index 806542b..af6cd2a 100644 --- a/image.darknet/src/layer.h +++ b/image.darknet/src/layer.h @@ -1,271 +1 @@ -#ifndef BASE_LAYER_H -#define BASE_LAYER_H - -#include "activations.h" -#include "stddef.h" -#include "tree.h" - -struct network_state; - -struct layer; -typedef struct layer layer; - -typedef enum { - CONVOLUTIONAL, - DECONVOLUTIONAL, - CONNECTED, - MAXPOOL, - SOFTMAX, - DETECTION, - DROPOUT, - CROP, - ROUTE, - COST, - NORMALIZATION, - AVGPOOL, - LOCAL, - SHORTCUT, - ACTIVE, - RNN, - GRU, - CRNN, - BATCHNORM, - NETWORK, - XNOR, - REGION, - REORG, - BLANK -} LAYER_TYPE; - -typedef enum{ - SSE, MASKED, SMOOTH -} COST_TYPE; - -struct layer{ - LAYER_TYPE type; - ACTIVATION activation; - COST_TYPE cost_type; - void (*forward) (struct layer, struct network_state); - void (*backward) (struct layer, struct network_state); - void (*update) (struct layer, int, float, float, float); - void (*forward_gpu) (struct layer, struct network_state); - void (*backward_gpu) (struct layer, struct network_state); - void (*update_gpu) (struct layer, int, float, float, float); - int batch_normalize; - int shortcut; - int batch; - int forced; - int flipped; - int inputs; - int outputs; - int truths; - int h,w,c; - int out_h, out_w, out_c; - int n; - int max_boxes; - int groups; - int size; - int side; - int stride; - int reverse; - int pad; - int sqrt; - int flip; - int index; - int binary; - int xnor; - int steps; - int hidden; - float dot; - float angle; - float jitter; - float saturation; - float exposure; - float shift; - float ratio; - int softmax; - int classes; - int coords; - int background; - int rescore; - int objectness; - int does_cost; - int joint; - int noadjust; - int reorg; - int log; - - int adam; - float B1; - float B2; - float eps; - int t; - - float alpha; - float beta; - float kappa; - - float coord_scale; - float object_scale; - float noobject_scale; - float class_scale; - int bias_match; - int random; - float thresh; - int classfix; - int absolute; - - int dontload; - int dontloadscales; - - float temperature; - float probability; - float scale; - - char * cweights; - int * indexes; - int * input_layers; - int * input_sizes; - int * map; - float * rand; - float * cost; - float * state; - float * prev_state; - float * forgot_state; - float * forgot_delta; - float * state_delta; - - float * concat; - float * concat_delta; - - float * binary_weights; - - float * biases; - float * bias_updates; - - float * scales; - float * scale_updates; - - float * weights; - float * weight_updates; - - float * col_image; - float * delta; - float * output; - float * squared; - float * norms; - - float * spatial_mean; - float * mean; - float * variance; - - float * mean_delta; - float * variance_delta; - - float * rolling_mean; - float * rolling_variance; - - float * x; - float * x_norm; - - float * m; - float * v; - - float * z_cpu; - float * r_cpu; - float * h_cpu; - - float * binary_input; - - struct layer *input_layer; - struct layer *self_layer; - struct layer *output_layer; - - struct layer *input_gate_layer; - struct layer *state_gate_layer; - struct layer *input_save_layer; - struct layer *state_save_layer; - struct layer *input_state_layer; - struct layer *state_state_layer; - - struct layer *input_z_layer; - struct layer *state_z_layer; - - struct layer *input_r_layer; - struct layer *state_r_layer; - - struct layer *input_h_layer; - struct layer *state_h_layer; - - tree *softmax_tree; - - size_t workspace_size; - - #ifdef GPU - int *indexes_gpu; - - float *z_gpu; - float *r_gpu; - float *h_gpu; - - float *m_gpu; - float *v_gpu; - - float * prev_state_gpu; - float * forgot_state_gpu; - float * forgot_delta_gpu; - float * state_gpu; - float * state_delta_gpu; - float * gate_gpu; - float * gate_delta_gpu; - float * save_gpu; - float * save_delta_gpu; - float * concat_gpu; - float * concat_delta_gpu; - - float *binary_input_gpu; - float *binary_weights_gpu; - - float * mean_gpu; - float * variance_gpu; - - float * rolling_mean_gpu; - float * rolling_variance_gpu; - - float * variance_delta_gpu; - float * mean_delta_gpu; - - float * col_image_gpu; - - float * x_gpu; - float * x_norm_gpu; - float * weights_gpu; - float * weight_updates_gpu; - - float * biases_gpu; - float * bias_updates_gpu; - - float * scales_gpu; - float * scale_updates_gpu; - - float * output_gpu; - float * delta_gpu; - float * rand_gpu; - float * squared_gpu; - float * norms_gpu; - #ifdef CUDNN - cudnnTensorDescriptor_t srcTensorDesc, dstTensorDesc; - cudnnTensorDescriptor_t dsrcTensorDesc, ddstTensorDesc; - cudnnFilterDescriptor_t weightDesc; - cudnnFilterDescriptor_t dweightDesc; - cudnnConvolutionDescriptor_t convDesc; - cudnnConvolutionFwdAlgo_t fw_algo; - cudnnConvolutionBwdDataAlgo_t bd_algo; - cudnnConvolutionBwdFilterAlgo_t bf_algo; - #endif - #endif -}; - -void free_layer(layer); - -#endif +#include "darknet.h" diff --git a/image.darknet/src/list.h b/image.darknet/src/list.h index fb818c2..6b445c7 100644 --- a/image.darknet/src/list.h +++ b/image.darknet/src/list.h @@ -1,26 +1,13 @@ #ifndef LIST_H #define LIST_H - -typedef struct node{ - void *val; - struct node *next; - struct node *prev; -} node; - -typedef struct list{ - int size; - node *front; - node *back; -} list; +#include "darknet.h" list *make_list(); int list_find(list *l, void *val); void list_insert(list *, void *); -void **list_to_array(list *l); -void free_list(list *l); void free_list_contents(list *l); #endif diff --git a/image.darknet/src/local_layer.c b/image.darknet/src/local_layer.c index 31f0ca6..74f6910 100644 --- a/image.darknet/src/local_layer.c +++ b/image.darknet/src/local_layer.c @@ -57,9 +57,10 @@ local_layer make_local_layer(int batch, int h, int w, int c, int n, int size, in float scale = sqrt(2./(size*size*c)); for(i = 0; i < c*n*size*size; ++i) l.weights[i] = scale*rand_uniform(-1,1); - l.col_image = calloc(out_h*out_w*size*size*c, sizeof(float)); l.output = calloc(l.batch*out_h * out_w * n, sizeof(float)); l.delta = calloc(l.batch*out_h * out_w * n, sizeof(float)); + + l.workspace_size = out_h*out_w*size*size*c; l.forward = forward_local_layer; l.backward = backward_local_layer; @@ -76,7 +77,6 @@ local_layer make_local_layer(int batch, int h, int w, int c, int n, int size, in l.biases_gpu = cuda_make_array(l.biases, l.outputs); l.bias_updates_gpu = cuda_make_array(l.bias_updates, l.outputs); - l.col_image_gpu = cuda_make_array(l.col_image, out_h*out_w*size*size*c); l.delta_gpu = cuda_make_array(l.delta, l.batch*out_h*out_w*n); l.output_gpu = cuda_make_array(l.output, l.batch*out_h*out_w*n); @@ -88,7 +88,7 @@ local_layer make_local_layer(int batch, int h, int w, int c, int n, int size, in return l; } -void forward_local_layer(const local_layer l, network_state state) +void forward_local_layer(const local_layer l, network net) { int out_h = local_out_height(l); int out_w = local_out_width(l); @@ -100,13 +100,13 @@ void forward_local_layer(const local_layer l, network_state state) } for(i = 0; i < l.batch; ++i){ - float *input = state.input + i*l.w*l.h*l.c; + float *input = net.input + i*l.w*l.h*l.c; im2col_cpu(input, l.c, l.h, l.w, - l.size, l.stride, l.pad, l.col_image); + l.size, l.stride, l.pad, net.workspace); float *output = l.output + i*l.outputs; for(j = 0; j < locations; ++j){ float *a = l.weights + j*l.size*l.size*l.c*l.n; - float *b = l.col_image + j; + float *b = net.workspace + j; float *c = output + j; int m = l.n; @@ -119,7 +119,7 @@ void forward_local_layer(const local_layer l, network_state state) activate_array(l.output, l.outputs*l.batch, l.activation); } -void backward_local_layer(local_layer l, network_state state) +void backward_local_layer(local_layer l, network net) { int i, j; int locations = l.out_w*l.out_h; @@ -131,13 +131,13 @@ void backward_local_layer(local_layer l, network_state state) } for(i = 0; i < l.batch; ++i){ - float *input = state.input + i*l.w*l.h*l.c; + float *input = net.input + i*l.w*l.h*l.c; im2col_cpu(input, l.c, l.h, l.w, - l.size, l.stride, l.pad, l.col_image); + l.size, l.stride, l.pad, net.workspace); for(j = 0; j < locations; ++j){ float *a = l.delta + i*l.outputs + j; - float *b = l.col_image + j; + float *b = net.workspace + j; float *c = l.weight_updates + j*l.size*l.size*l.c*l.n; int m = l.n; int n = l.size*l.size*l.c; @@ -146,11 +146,11 @@ void backward_local_layer(local_layer l, network_state state) gemm(0,1,m,n,k,1,a,locations,b,locations,1,c,n); } - if(state.delta){ + if(net.delta){ for(j = 0; j < locations; ++j){ float *a = l.weights + j*l.size*l.size*l.c*l.n; float *b = l.delta + i*l.outputs + j; - float *c = l.col_image + j; + float *c = net.workspace + j; int m = l.size*l.size*l.c; int n = 1; @@ -159,13 +159,18 @@ void backward_local_layer(local_layer l, network_state state) gemm(1,0,m,n,k,1,a,m,b,locations,0,c,locations); } - col2im_cpu(l.col_image, l.c, l.h, l.w, l.size, l.stride, l.pad, state.delta+i*l.c*l.h*l.w); + col2im_cpu(net.workspace, l.c, l.h, l.w, l.size, l.stride, l.pad, net.delta+i*l.c*l.h*l.w); } } } -void update_local_layer(local_layer l, int batch, float learning_rate, float momentum, float decay) +void update_local_layer(local_layer l, update_args a) { + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + int locations = l.out_w*l.out_h; int size = l.size*l.size*l.c*l.n*locations; axpy_cpu(l.outputs, learning_rate/batch, l.bias_updates, 1, l.biases, 1); @@ -178,7 +183,7 @@ void update_local_layer(local_layer l, int batch, float learning_rate, float mom #ifdef GPU -void forward_local_layer_gpu(const local_layer l, network_state state) +void forward_local_layer_gpu(const local_layer l, network net) { int out_h = local_out_height(l); int out_w = local_out_width(l); @@ -186,83 +191,88 @@ void forward_local_layer_gpu(const local_layer l, network_state state) int locations = out_h * out_w; for(i = 0; i < l.batch; ++i){ - copy_ongpu(l.outputs, l.biases_gpu, 1, l.output_gpu + i*l.outputs, 1); + copy_gpu(l.outputs, l.biases_gpu, 1, l.output_gpu + i*l.outputs, 1); } for(i = 0; i < l.batch; ++i){ - float *input = state.input + i*l.w*l.h*l.c; - im2col_ongpu(input, l.c, l.h, l.w, - l.size, l.stride, l.pad, l.col_image_gpu); + float *input = net.input_gpu + i*l.w*l.h*l.c; + im2col_gpu(input, l.c, l.h, l.w, + l.size, l.stride, l.pad, net.workspace); float *output = l.output_gpu + i*l.outputs; for(j = 0; j < locations; ++j){ float *a = l.weights_gpu + j*l.size*l.size*l.c*l.n; - float *b = l.col_image_gpu + j; + float *b = net.workspace + j; float *c = output + j; int m = l.n; int n = 1; int k = l.size*l.size*l.c; - gemm_ongpu(0,0,m,n,k,1,a,k,b,locations,1,c,locations); + gemm_gpu(0,0,m,n,k,1,a,k,b,locations,1,c,locations); } } - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); } -void backward_local_layer_gpu(local_layer l, network_state state) +void backward_local_layer_gpu(local_layer l, network net) { int i, j; int locations = l.out_w*l.out_h; - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); for(i = 0; i < l.batch; ++i){ - axpy_ongpu(l.outputs, 1, l.delta_gpu + i*l.outputs, 1, l.bias_updates_gpu, 1); + axpy_gpu(l.outputs, 1, l.delta_gpu + i*l.outputs, 1, l.bias_updates_gpu, 1); } for(i = 0; i < l.batch; ++i){ - float *input = state.input + i*l.w*l.h*l.c; - im2col_ongpu(input, l.c, l.h, l.w, - l.size, l.stride, l.pad, l.col_image_gpu); + float *input = net.input_gpu + i*l.w*l.h*l.c; + im2col_gpu(input, l.c, l.h, l.w, + l.size, l.stride, l.pad, net.workspace); for(j = 0; j < locations; ++j){ float *a = l.delta_gpu + i*l.outputs + j; - float *b = l.col_image_gpu + j; + float *b = net.workspace + j; float *c = l.weight_updates_gpu + j*l.size*l.size*l.c*l.n; int m = l.n; int n = l.size*l.size*l.c; int k = 1; - gemm_ongpu(0,1,m,n,k,1,a,locations,b,locations,1,c,n); + gemm_gpu(0,1,m,n,k,1,a,locations,b,locations,1,c,n); } - if(state.delta){ + if(net.delta_gpu){ for(j = 0; j < locations; ++j){ float *a = l.weights_gpu + j*l.size*l.size*l.c*l.n; float *b = l.delta_gpu + i*l.outputs + j; - float *c = l.col_image_gpu + j; + float *c = net.workspace + j; int m = l.size*l.size*l.c; int n = 1; int k = l.n; - gemm_ongpu(1,0,m,n,k,1,a,m,b,locations,0,c,locations); + gemm_gpu(1,0,m,n,k,1,a,m,b,locations,0,c,locations); } - col2im_ongpu(l.col_image_gpu, l.c, l.h, l.w, l.size, l.stride, l.pad, state.delta+i*l.c*l.h*l.w); + col2im_gpu(net.workspace, l.c, l.h, l.w, l.size, l.stride, l.pad, net.delta_gpu+i*l.c*l.h*l.w); } } } -void update_local_layer_gpu(local_layer l, int batch, float learning_rate, float momentum, float decay) +void update_local_layer_gpu(local_layer l, update_args a) { + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + int locations = l.out_w*l.out_h; int size = l.size*l.size*l.c*l.n*locations; - axpy_ongpu(l.outputs, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); - scal_ongpu(l.outputs, momentum, l.bias_updates_gpu, 1); + axpy_gpu(l.outputs, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); + scal_gpu(l.outputs, momentum, l.bias_updates_gpu, 1); - axpy_ongpu(size, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); - axpy_ongpu(size, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); - scal_ongpu(size, momentum, l.weight_updates_gpu, 1); + axpy_gpu(size, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); + axpy_gpu(size, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); + scal_gpu(size, momentum, l.weight_updates_gpu, 1); } void pull_local_layer(local_layer l) diff --git a/image.darknet/src/local_layer.h b/image.darknet/src/local_layer.h index 28915d8..776e572 100644 --- a/image.darknet/src/local_layer.h +++ b/image.darknet/src/local_layer.h @@ -10,9 +10,9 @@ typedef layer local_layer; #ifdef GPU -void forward_local_layer_gpu(local_layer layer, network_state state); -void backward_local_layer_gpu(local_layer layer, network_state state); -void update_local_layer_gpu(local_layer layer, int batch, float learning_rate, float momentum, float decay); +void forward_local_layer_gpu(local_layer layer, network net); +void backward_local_layer_gpu(local_layer layer, network net); +void update_local_layer_gpu(local_layer layer, update_args a); void push_local_layer(local_layer layer); void pull_local_layer(local_layer layer); @@ -20,9 +20,9 @@ void pull_local_layer(local_layer layer); local_layer make_local_layer(int batch, int h, int w, int c, int n, int size, int stride, int pad, ACTIVATION activation); -void forward_local_layer(const local_layer layer, network_state state); -void backward_local_layer(local_layer layer, network_state state); -void update_local_layer(local_layer layer, int batch, float learning_rate, float momentum, float decay); +void forward_local_layer(const local_layer layer, network net); +void backward_local_layer(local_layer layer, network net); +void update_local_layer(local_layer layer, update_args a); void bias_output(float *output, float *biases, int batch, int n, int size); void backward_bias(float *bias_updates, float *delta, int batch, int n, int size); diff --git a/image.darknet/src/logistic_layer.c b/image.darknet/src/logistic_layer.c new file mode 100644 index 0000000..b2b3d6b --- /dev/null +++ b/image.darknet/src/logistic_layer.c @@ -0,0 +1,71 @@ +#include "logistic_layer.h" +#include "activations.h" +#include "blas.h" +#include "cuda.h" + +#include +#include +#include +#include +#include + +layer make_logistic_layer(int batch, int inputs) +{ + fprintf(stderr, "logistic x entropy %4d\n", inputs); + layer l = {0}; + l.type = LOGXENT; + l.batch = batch; + l.inputs = inputs; + l.outputs = inputs; + l.loss = calloc(inputs*batch, sizeof(float)); + l.output = calloc(inputs*batch, sizeof(float)); + l.delta = calloc(inputs*batch, sizeof(float)); + l.cost = calloc(1, sizeof(float)); + + l.forward = forward_logistic_layer; + l.backward = backward_logistic_layer; + #ifdef GPU + l.forward_gpu = forward_logistic_layer_gpu; + l.backward_gpu = backward_logistic_layer_gpu; + + l.output_gpu = cuda_make_array(l.output, inputs*batch); + l.loss_gpu = cuda_make_array(l.loss, inputs*batch); + l.delta_gpu = cuda_make_array(l.delta, inputs*batch); + #endif + return l; +} + +void forward_logistic_layer(const layer l, network net) +{ + copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); + activate_array(l.output, l.outputs*l.batch, LOGISTIC); + if(net.truth){ + logistic_x_ent_cpu(l.batch*l.inputs, l.output, net.truth, l.delta, l.loss); + l.cost[0] = sum_array(l.loss, l.batch*l.inputs); + } +} + +void backward_logistic_layer(const layer l, network net) +{ + axpy_cpu(l.inputs*l.batch, 1, l.delta, 1, net.delta, 1); +} + +#ifdef GPU + +void forward_logistic_layer_gpu(const layer l, network net) +{ + copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, LOGISTIC); + if(net.truth){ + logistic_x_ent_gpu(l.batch*l.inputs, l.output_gpu, net.truth_gpu, l.delta_gpu, l.loss_gpu); + cuda_pull_array(l.loss_gpu, l.loss, l.batch*l.inputs); + l.cost[0] = sum_array(l.loss, l.batch*l.inputs); + } +} + +void backward_logistic_layer_gpu(const layer l, network net) +{ + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); +} + +#endif diff --git a/image.darknet/src/logistic_layer.h b/image.darknet/src/logistic_layer.h new file mode 100644 index 0000000..9c25bee --- /dev/null +++ b/image.darknet/src/logistic_layer.h @@ -0,0 +1,15 @@ +#ifndef LOGISTIC_LAYER_H +#define LOGISTIC_LAYER_H +#include "layer.h" +#include "network.h" + +layer make_logistic_layer(int batch, int inputs); +void forward_logistic_layer(const layer l, network net); +void backward_logistic_layer(const layer l, network net); + +#ifdef GPU +void forward_logistic_layer_gpu(const layer l, network net); +void backward_logistic_layer_gpu(const layer l, network net); +#endif + +#endif diff --git a/image.darknet/src/lstm_layer.c b/image.darknet/src/lstm_layer.c new file mode 100644 index 0000000..fb07de2 --- /dev/null +++ b/image.darknet/src/lstm_layer.c @@ -0,0 +1,626 @@ +#include "lstm_layer.h" +#include "connected_layer.h" +#include "utils.h" +#include "cuda.h" +#include "blas.h" +#include "gemm.h" + +#include +#include +#include +#include + +static void increment_layer(layer *l, int steps) +{ + int num = l->outputs*l->batch*steps; + l->output += num; + l->delta += num; + l->x += num; + l->x_norm += num; + +#ifdef GPU + l->output_gpu += num; + l->delta_gpu += num; + l->x_gpu += num; + l->x_norm_gpu += num; +#endif +} + +layer make_lstm_layer(int batch, int inputs, int outputs, int steps, int batch_normalize, int adam) +{ + fprintf(stderr, "LSTM Layer: %d inputs, %d outputs\n", inputs, outputs); + batch = batch / steps; + layer l = { 0 }; + l.batch = batch; + l.type = LSTM; + l.steps = steps; + l.inputs = inputs; + + l.uf = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.uf) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.uf->batch = batch; + + l.ui = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.ui) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.ui->batch = batch; + + l.ug = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.ug) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.ug->batch = batch; + + l.uo = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.uo) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.uo->batch = batch; + + l.wf = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.wf) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wf->batch = batch; + + l.wi = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.wi) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wi->batch = batch; + + l.wg = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.wg) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wg->batch = batch; + + l.wo = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.wo) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wo->batch = batch; + + l.batch_normalize = batch_normalize; + l.outputs = outputs; + + l.output = calloc(outputs*batch*steps, sizeof(float)); + l.state = calloc(outputs*batch, sizeof(float)); + + l.forward = forward_lstm_layer; + l.update = update_lstm_layer; + + l.prev_state_cpu = calloc(batch*outputs, sizeof(float)); + l.prev_cell_cpu = calloc(batch*outputs, sizeof(float)); + l.cell_cpu = calloc(batch*outputs*steps, sizeof(float)); + + l.f_cpu = calloc(batch*outputs, sizeof(float)); + l.i_cpu = calloc(batch*outputs, sizeof(float)); + l.g_cpu = calloc(batch*outputs, sizeof(float)); + l.o_cpu = calloc(batch*outputs, sizeof(float)); + l.c_cpu = calloc(batch*outputs, sizeof(float)); + l.h_cpu = calloc(batch*outputs, sizeof(float)); + l.temp_cpu = calloc(batch*outputs, sizeof(float)); + l.temp2_cpu = calloc(batch*outputs, sizeof(float)); + l.temp3_cpu = calloc(batch*outputs, sizeof(float)); + l.dc_cpu = calloc(batch*outputs, sizeof(float)); + l.dh_cpu = calloc(batch*outputs, sizeof(float)); + +#ifdef GPU + l.forward_gpu = forward_lstm_layer_gpu; + l.backward_gpu = backward_lstm_layer_gpu; + l.update_gpu = update_lstm_layer_gpu; + + l.output_gpu = cuda_make_array(0, batch*outputs*steps); + l.delta_gpu = cuda_make_array(0, batch*l.outputs*steps); + + l.prev_state_gpu = cuda_make_array(0, batch*outputs); + l.prev_cell_gpu = cuda_make_array(0, batch*outputs); + l.cell_gpu = cuda_make_array(0, batch*outputs*steps); + + l.f_gpu = cuda_make_array(0, batch*outputs); + l.i_gpu = cuda_make_array(0, batch*outputs); + l.g_gpu = cuda_make_array(0, batch*outputs); + l.o_gpu = cuda_make_array(0, batch*outputs); + l.c_gpu = cuda_make_array(0, batch*outputs); + l.h_gpu = cuda_make_array(0, batch*outputs); + l.temp_gpu = cuda_make_array(0, batch*outputs); + l.temp2_gpu = cuda_make_array(0, batch*outputs); + l.temp3_gpu = cuda_make_array(0, batch*outputs); + l.dc_gpu = cuda_make_array(0, batch*outputs); + l.dh_gpu = cuda_make_array(0, batch*outputs); +#ifdef CUDNN + cudnnSetTensor4dDescriptor(l.wf->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wf->out_c, l.wf->out_h, l.wf->out_w); + cudnnSetTensor4dDescriptor(l.wi->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wi->out_c, l.wi->out_h, l.wi->out_w); + cudnnSetTensor4dDescriptor(l.wg->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wg->out_c, l.wg->out_h, l.wg->out_w); + cudnnSetTensor4dDescriptor(l.wo->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wo->out_c, l.wo->out_h, l.wo->out_w); + + cudnnSetTensor4dDescriptor(l.uf->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.uf->out_c, l.uf->out_h, l.uf->out_w); + cudnnSetTensor4dDescriptor(l.ui->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.ui->out_c, l.ui->out_h, l.ui->out_w); + cudnnSetTensor4dDescriptor(l.ug->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.ug->out_c, l.ug->out_h, l.ug->out_w); + cudnnSetTensor4dDescriptor(l.uo->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.uo->out_c, l.uo->out_h, l.uo->out_w); +#endif + +#endif + + return l; +} + +void update_lstm_layer(layer l, update_args a) +{ + update_connected_layer(*(l.wf), a); + update_connected_layer(*(l.wi), a); + update_connected_layer(*(l.wg), a); + update_connected_layer(*(l.wo), a); + update_connected_layer(*(l.uf), a); + update_connected_layer(*(l.ui), a); + update_connected_layer(*(l.ug), a); + update_connected_layer(*(l.uo), a); +} + +void forward_lstm_layer(layer l, network state) +{ + network s = { 0 }; + s.train = state.train; + int i; + layer wf = *(l.wf); + layer wi = *(l.wi); + layer wg = *(l.wg); + layer wo = *(l.wo); + + layer uf = *(l.uf); + layer ui = *(l.ui); + layer ug = *(l.ug); + layer uo = *(l.uo); + + fill_cpu(l.outputs * l.batch * l.steps, 0, wf.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wi.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wg.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wo.delta, 1); + + fill_cpu(l.outputs * l.batch * l.steps, 0, uf.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, ui.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, ug.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, uo.delta, 1); + if (state.train) { + fill_cpu(l.outputs * l.batch * l.steps, 0, l.delta, 1); + } + + for (i = 0; i < l.steps; ++i) { + s.input = l.h_cpu; + forward_connected_layer(wf, s); + forward_connected_layer(wi, s); + forward_connected_layer(wg, s); + forward_connected_layer(wo, s); + + s.input = state.input; + forward_connected_layer(uf, s); + forward_connected_layer(ui, s); + forward_connected_layer(ug, s); + forward_connected_layer(uo, s); + + copy_cpu(l.outputs*l.batch, wf.output, 1, l.f_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, uf.output, 1, l.f_cpu, 1); + + copy_cpu(l.outputs*l.batch, wi.output, 1, l.i_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, ui.output, 1, l.i_cpu, 1); + + copy_cpu(l.outputs*l.batch, wg.output, 1, l.g_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, ug.output, 1, l.g_cpu, 1); + + copy_cpu(l.outputs*l.batch, wo.output, 1, l.o_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, uo.output, 1, l.o_cpu, 1); + + activate_array(l.f_cpu, l.outputs*l.batch, LOGISTIC); + activate_array(l.i_cpu, l.outputs*l.batch, LOGISTIC); + activate_array(l.g_cpu, l.outputs*l.batch, TANH); + activate_array(l.o_cpu, l.outputs*l.batch, LOGISTIC); + + copy_cpu(l.outputs*l.batch, l.i_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.g_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.f_cpu, 1, l.c_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, l.temp_cpu, 1, l.c_cpu, 1); + + copy_cpu(l.outputs*l.batch, l.c_cpu, 1, l.h_cpu, 1); + activate_array(l.h_cpu, l.outputs*l.batch, TANH); + mul_cpu(l.outputs*l.batch, l.o_cpu, 1, l.h_cpu, 1); + + copy_cpu(l.outputs*l.batch, l.c_cpu, 1, l.cell_cpu, 1); + copy_cpu(l.outputs*l.batch, l.h_cpu, 1, l.output, 1); + + state.input += l.inputs*l.batch; + l.output += l.outputs*l.batch; + l.cell_cpu += l.outputs*l.batch; + + increment_layer(&wf, 1); + increment_layer(&wi, 1); + increment_layer(&wg, 1); + increment_layer(&wo, 1); + + increment_layer(&uf, 1); + increment_layer(&ui, 1); + increment_layer(&ug, 1); + increment_layer(&uo, 1); + } +} + +void backward_lstm_layer(layer l, network state) +{ + network s = { 0 }; + s.train = state.train; + int i; + layer wf = *(l.wf); + layer wi = *(l.wi); + layer wg = *(l.wg); + layer wo = *(l.wo); + + layer uf = *(l.uf); + layer ui = *(l.ui); + layer ug = *(l.ug); + layer uo = *(l.uo); + + increment_layer(&wf, l.steps - 1); + increment_layer(&wi, l.steps - 1); + increment_layer(&wg, l.steps - 1); + increment_layer(&wo, l.steps - 1); + + increment_layer(&uf, l.steps - 1); + increment_layer(&ui, l.steps - 1); + increment_layer(&ug, l.steps - 1); + increment_layer(&uo, l.steps - 1); + + state.input += l.inputs*l.batch*(l.steps - 1); + if (state.delta) state.delta += l.inputs*l.batch*(l.steps - 1); + + l.output += l.outputs*l.batch*(l.steps - 1); + l.cell_cpu += l.outputs*l.batch*(l.steps - 1); + l.delta += l.outputs*l.batch*(l.steps - 1); + + for (i = l.steps - 1; i >= 0; --i) { + if (i != 0) copy_cpu(l.outputs*l.batch, l.cell_cpu - l.outputs*l.batch, 1, l.prev_cell_cpu, 1); + copy_cpu(l.outputs*l.batch, l.cell_cpu, 1, l.c_cpu, 1); + if (i != 0) copy_cpu(l.outputs*l.batch, l.output - l.outputs*l.batch, 1, l.prev_state_cpu, 1); + copy_cpu(l.outputs*l.batch, l.output, 1, l.h_cpu, 1); + + l.dh_cpu = (i == 0) ? 0 : l.delta - l.outputs*l.batch; + + copy_cpu(l.outputs*l.batch, wf.output, 1, l.f_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, uf.output, 1, l.f_cpu, 1); + + copy_cpu(l.outputs*l.batch, wi.output, 1, l.i_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, ui.output, 1, l.i_cpu, 1); + + copy_cpu(l.outputs*l.batch, wg.output, 1, l.g_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, ug.output, 1, l.g_cpu, 1); + + copy_cpu(l.outputs*l.batch, wo.output, 1, l.o_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, uo.output, 1, l.o_cpu, 1); + + activate_array(l.f_cpu, l.outputs*l.batch, LOGISTIC); + activate_array(l.i_cpu, l.outputs*l.batch, LOGISTIC); + activate_array(l.g_cpu, l.outputs*l.batch, TANH); + activate_array(l.o_cpu, l.outputs*l.batch, LOGISTIC); + + copy_cpu(l.outputs*l.batch, l.delta, 1, l.temp3_cpu, 1); + + copy_cpu(l.outputs*l.batch, l.c_cpu, 1, l.temp_cpu, 1); + activate_array(l.temp_cpu, l.outputs*l.batch, TANH); + + copy_cpu(l.outputs*l.batch, l.temp3_cpu, 1, l.temp2_cpu, 1); + mul_cpu(l.outputs*l.batch, l.o_cpu, 1, l.temp2_cpu, 1); + + gradient_array(l.temp_cpu, l.outputs*l.batch, TANH, l.temp2_cpu); + axpy_cpu(l.outputs*l.batch, 1, l.dc_cpu, 1, l.temp2_cpu, 1); + + copy_cpu(l.outputs*l.batch, l.c_cpu, 1, l.temp_cpu, 1); + activate_array(l.temp_cpu, l.outputs*l.batch, TANH); + mul_cpu(l.outputs*l.batch, l.temp3_cpu, 1, l.temp_cpu, 1); + gradient_array(l.o_cpu, l.outputs*l.batch, LOGISTIC, l.temp_cpu); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, wo.delta, 1); + s.input = l.prev_state_cpu; + s.delta = l.dh_cpu; + backward_connected_layer(wo, s); + + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, uo.delta, 1); + s.input = state.input; + s.delta = state.delta; + backward_connected_layer(uo, s); + + copy_cpu(l.outputs*l.batch, l.temp2_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.i_cpu, 1, l.temp_cpu, 1); + gradient_array(l.g_cpu, l.outputs*l.batch, TANH, l.temp_cpu); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, wg.delta, 1); + s.input = l.prev_state_cpu; + s.delta = l.dh_cpu; + backward_connected_layer(wg, s); + + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, ug.delta, 1); + s.input = state.input; + s.delta = state.delta; + backward_connected_layer(ug, s); + + copy_cpu(l.outputs*l.batch, l.temp2_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.g_cpu, 1, l.temp_cpu, 1); + gradient_array(l.i_cpu, l.outputs*l.batch, LOGISTIC, l.temp_cpu); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, wi.delta, 1); + s.input = l.prev_state_cpu; + s.delta = l.dh_cpu; + backward_connected_layer(wi, s); + + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, ui.delta, 1); + s.input = state.input; + s.delta = state.delta; + backward_connected_layer(ui, s); + + copy_cpu(l.outputs*l.batch, l.temp2_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.prev_cell_cpu, 1, l.temp_cpu, 1); + gradient_array(l.f_cpu, l.outputs*l.batch, LOGISTIC, l.temp_cpu); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, wf.delta, 1); + s.input = l.prev_state_cpu; + s.delta = l.dh_cpu; + backward_connected_layer(wf, s); + + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, uf.delta, 1); + s.input = state.input; + s.delta = state.delta; + backward_connected_layer(uf, s); + + copy_cpu(l.outputs*l.batch, l.temp2_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.f_cpu, 1, l.temp_cpu, 1); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, l.dc_cpu, 1); + + state.input -= l.inputs*l.batch; + if (state.delta) state.delta -= l.inputs*l.batch; + l.output -= l.outputs*l.batch; + l.cell_cpu -= l.outputs*l.batch; + l.delta -= l.outputs*l.batch; + + increment_layer(&wf, -1); + increment_layer(&wi, -1); + increment_layer(&wg, -1); + increment_layer(&wo, -1); + + increment_layer(&uf, -1); + increment_layer(&ui, -1); + increment_layer(&ug, -1); + increment_layer(&uo, -1); + } +} + +#ifdef GPU +void update_lstm_layer_gpu(layer l, update_args a) +{ + update_connected_layer_gpu(*(l.wf), a); + update_connected_layer_gpu(*(l.wi), a); + update_connected_layer_gpu(*(l.wg), a); + update_connected_layer_gpu(*(l.wo), a); + update_connected_layer_gpu(*(l.uf), a); + update_connected_layer_gpu(*(l.ui), a); + update_connected_layer_gpu(*(l.ug), a); + update_connected_layer_gpu(*(l.uo), a); +} + +void forward_lstm_layer_gpu(layer l, network state) +{ + network s = { 0 }; + s.train = state.train; + int i; + layer wf = *(l.wf); + layer wi = *(l.wi); + layer wg = *(l.wg); + layer wo = *(l.wo); + + layer uf = *(l.uf); + layer ui = *(l.ui); + layer ug = *(l.ug); + layer uo = *(l.uo); + + fill_gpu(l.outputs * l.batch * l.steps, 0, wf.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wi.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wg.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wo.delta_gpu, 1); + + fill_gpu(l.outputs * l.batch * l.steps, 0, uf.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, ui.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, ug.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, uo.delta_gpu, 1); + if (state.train) { + fill_gpu(l.outputs * l.batch * l.steps, 0, l.delta_gpu, 1); + } + + for (i = 0; i < l.steps; ++i) { + s.input_gpu = l.h_gpu; + forward_connected_layer_gpu(wf, s); + forward_connected_layer_gpu(wi, s); + forward_connected_layer_gpu(wg, s); + forward_connected_layer_gpu(wo, s); + + s.input_gpu = state.input_gpu; + forward_connected_layer_gpu(uf, s); + forward_connected_layer_gpu(ui, s); + forward_connected_layer_gpu(ug, s); + forward_connected_layer_gpu(uo, s); + + copy_gpu(l.outputs*l.batch, wf.output_gpu, 1, l.f_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, uf.output_gpu, 1, l.f_gpu, 1); + + copy_gpu(l.outputs*l.batch, wi.output_gpu, 1, l.i_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, ui.output_gpu, 1, l.i_gpu, 1); + + copy_gpu(l.outputs*l.batch, wg.output_gpu, 1, l.g_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, ug.output_gpu, 1, l.g_gpu, 1); + + copy_gpu(l.outputs*l.batch, wo.output_gpu, 1, l.o_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, uo.output_gpu, 1, l.o_gpu, 1); + + activate_array_gpu(l.f_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.i_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.g_gpu, l.outputs*l.batch, TANH); + activate_array_gpu(l.o_gpu, l.outputs*l.batch, LOGISTIC); + + copy_gpu(l.outputs*l.batch, l.i_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.g_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.f_gpu, 1, l.c_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, l.temp_gpu, 1, l.c_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.c_gpu, 1, l.h_gpu, 1); + activate_array_gpu(l.h_gpu, l.outputs*l.batch, TANH); + mul_gpu(l.outputs*l.batch, l.o_gpu, 1, l.h_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.c_gpu, 1, l.cell_gpu, 1); + copy_gpu(l.outputs*l.batch, l.h_gpu, 1, l.output_gpu, 1); + + state.input_gpu += l.inputs*l.batch; + l.output_gpu += l.outputs*l.batch; + l.cell_gpu += l.outputs*l.batch; + + increment_layer(&wf, 1); + increment_layer(&wi, 1); + increment_layer(&wg, 1); + increment_layer(&wo, 1); + + increment_layer(&uf, 1); + increment_layer(&ui, 1); + increment_layer(&ug, 1); + increment_layer(&uo, 1); + } +} + +void backward_lstm_layer_gpu(layer l, network state) +{ + network s = { 0 }; + s.train = state.train; + int i; + layer wf = *(l.wf); + layer wi = *(l.wi); + layer wg = *(l.wg); + layer wo = *(l.wo); + + layer uf = *(l.uf); + layer ui = *(l.ui); + layer ug = *(l.ug); + layer uo = *(l.uo); + + increment_layer(&wf, l.steps - 1); + increment_layer(&wi, l.steps - 1); + increment_layer(&wg, l.steps - 1); + increment_layer(&wo, l.steps - 1); + + increment_layer(&uf, l.steps - 1); + increment_layer(&ui, l.steps - 1); + increment_layer(&ug, l.steps - 1); + increment_layer(&uo, l.steps - 1); + + state.input_gpu += l.inputs*l.batch*(l.steps - 1); + if (state.delta_gpu) state.delta_gpu += l.inputs*l.batch*(l.steps - 1); + + l.output_gpu += l.outputs*l.batch*(l.steps - 1); + l.cell_gpu += l.outputs*l.batch*(l.steps - 1); + l.delta_gpu += l.outputs*l.batch*(l.steps - 1); + + for (i = l.steps - 1; i >= 0; --i) { + if (i != 0) copy_gpu(l.outputs*l.batch, l.cell_gpu - l.outputs*l.batch, 1, l.prev_cell_gpu, 1); + copy_gpu(l.outputs*l.batch, l.cell_gpu, 1, l.c_gpu, 1); + if (i != 0) copy_gpu(l.outputs*l.batch, l.output_gpu - l.outputs*l.batch, 1, l.prev_state_gpu, 1); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.h_gpu, 1); + + l.dh_gpu = (i == 0) ? 0 : l.delta_gpu - l.outputs*l.batch; + + copy_gpu(l.outputs*l.batch, wf.output_gpu, 1, l.f_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, uf.output_gpu, 1, l.f_gpu, 1); + + copy_gpu(l.outputs*l.batch, wi.output_gpu, 1, l.i_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, ui.output_gpu, 1, l.i_gpu, 1); + + copy_gpu(l.outputs*l.batch, wg.output_gpu, 1, l.g_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, ug.output_gpu, 1, l.g_gpu, 1); + + copy_gpu(l.outputs*l.batch, wo.output_gpu, 1, l.o_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, uo.output_gpu, 1, l.o_gpu, 1); + + activate_array_gpu(l.f_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.i_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.g_gpu, l.outputs*l.batch, TANH); + activate_array_gpu(l.o_gpu, l.outputs*l.batch, LOGISTIC); + + copy_gpu(l.outputs*l.batch, l.delta_gpu, 1, l.temp3_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.c_gpu, 1, l.temp_gpu, 1); + activate_array_gpu(l.temp_gpu, l.outputs*l.batch, TANH); + + copy_gpu(l.outputs*l.batch, l.temp3_gpu, 1, l.temp2_gpu, 1); + mul_gpu(l.outputs*l.batch, l.o_gpu, 1, l.temp2_gpu, 1); + + gradient_array_gpu(l.temp_gpu, l.outputs*l.batch, TANH, l.temp2_gpu); + axpy_gpu(l.outputs*l.batch, 1, l.dc_gpu, 1, l.temp2_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.c_gpu, 1, l.temp_gpu, 1); + activate_array_gpu(l.temp_gpu, l.outputs*l.batch, TANH); + mul_gpu(l.outputs*l.batch, l.temp3_gpu, 1, l.temp_gpu, 1); + gradient_array_gpu(l.o_gpu, l.outputs*l.batch, LOGISTIC, l.temp_gpu); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, wo.delta_gpu, 1); + s.input_gpu = l.prev_state_gpu; + s.delta_gpu = l.dh_gpu; + backward_connected_layer_gpu(wo, s); + + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, uo.delta_gpu, 1); + s.input_gpu = state.input_gpu; + s.delta_gpu = state.delta_gpu; + backward_connected_layer_gpu(uo, s); + + copy_gpu(l.outputs*l.batch, l.temp2_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.i_gpu, 1, l.temp_gpu, 1); + gradient_array_gpu(l.g_gpu, l.outputs*l.batch, TANH, l.temp_gpu); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, wg.delta_gpu, 1); + s.input_gpu = l.prev_state_gpu; + s.delta_gpu = l.dh_gpu; + backward_connected_layer_gpu(wg, s); + + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, ug.delta_gpu, 1); + s.input_gpu = state.input_gpu; + s.delta_gpu = state.delta_gpu; + backward_connected_layer_gpu(ug, s); + + copy_gpu(l.outputs*l.batch, l.temp2_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.g_gpu, 1, l.temp_gpu, 1); + gradient_array_gpu(l.i_gpu, l.outputs*l.batch, LOGISTIC, l.temp_gpu); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, wi.delta_gpu, 1); + s.input_gpu = l.prev_state_gpu; + s.delta_gpu = l.dh_gpu; + backward_connected_layer_gpu(wi, s); + + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, ui.delta_gpu, 1); + s.input_gpu = state.input_gpu; + s.delta_gpu = state.delta_gpu; + backward_connected_layer_gpu(ui, s); + + copy_gpu(l.outputs*l.batch, l.temp2_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.prev_cell_gpu, 1, l.temp_gpu, 1); + gradient_array_gpu(l.f_gpu, l.outputs*l.batch, LOGISTIC, l.temp_gpu); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, wf.delta_gpu, 1); + s.input_gpu = l.prev_state_gpu; + s.delta_gpu = l.dh_gpu; + backward_connected_layer_gpu(wf, s); + + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, uf.delta_gpu, 1); + s.input_gpu = state.input_gpu; + s.delta_gpu = state.delta_gpu; + backward_connected_layer_gpu(uf, s); + + copy_gpu(l.outputs*l.batch, l.temp2_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.f_gpu, 1, l.temp_gpu, 1); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, l.dc_gpu, 1); + + state.input_gpu -= l.inputs*l.batch; + if (state.delta_gpu) state.delta_gpu -= l.inputs*l.batch; + l.output_gpu -= l.outputs*l.batch; + l.cell_gpu -= l.outputs*l.batch; + l.delta_gpu -= l.outputs*l.batch; + + increment_layer(&wf, -1); + increment_layer(&wi, -1); + increment_layer(&wg, -1); + increment_layer(&wo, -1); + + increment_layer(&uf, -1); + increment_layer(&ui, -1); + increment_layer(&ug, -1); + increment_layer(&uo, -1); + } +} +#endif diff --git a/image.darknet/src/lstm_layer.h b/image.darknet/src/lstm_layer.h new file mode 100644 index 0000000..b9f07e6 --- /dev/null +++ b/image.darknet/src/lstm_layer.h @@ -0,0 +1,20 @@ +#ifndef LSTM_LAYER_H +#define LSTM_LAYER_H + +#include "activations.h" +#include "layer.h" +#include "network.h" +#define USET + +layer make_lstm_layer(int batch, int inputs, int outputs, int steps, int batch_normalize, int adam); + +void forward_lstm_layer(layer l, network net); +void update_lstm_layer(layer l, update_args a); + +#ifdef GPU +void forward_lstm_layer_gpu(layer l, network net); +void backward_lstm_layer_gpu(layer l, network net); +void update_lstm_layer_gpu(layer l, update_args a); + +#endif +#endif diff --git a/image.darknet/src/matrix.c b/image.darknet/src/matrix.c index ee14979..799916b 100644 --- a/image.darknet/src/matrix.c +++ b/image.darknet/src/matrix.c @@ -1,5 +1,6 @@ #include "matrix.h" #include "utils.h" +#include "blas.h" #include #include #include @@ -73,6 +74,20 @@ void matrix_add_matrix(matrix from, matrix to) } } +matrix copy_matrix(matrix m) +{ + matrix c = {0}; + c.rows = m.rows; + c.cols = m.cols; + c.vals = calloc(c.rows, sizeof(float *)); + int i; + for(i = 0; i < c.rows; ++i){ + c.vals[i] = calloc(c.cols, sizeof(float)); + copy_cpu(c.cols, m.vals[i], 1, c.vals[i], 1); + } + return c; +} + matrix make_matrix(int rows, int cols) { int i; diff --git a/image.darknet/src/matrix.h b/image.darknet/src/matrix.h index 641b596..879acd7 100644 --- a/image.darknet/src/matrix.h +++ b/image.darknet/src/matrix.h @@ -1,20 +1,11 @@ #ifndef MATRIX_H #define MATRIX_H -typedef struct matrix{ - int rows, cols; - float **vals; -} matrix; +#include "darknet.h" -matrix make_matrix(int rows, int cols); -void free_matrix(matrix m); +matrix copy_matrix(matrix m); void print_matrix(matrix m); -matrix csv_to_matrix(char *filename); -void matrix_to_csv(matrix m); matrix hold_out_matrix(matrix *m, int n); -float matrix_topk_accuracy(matrix truth, matrix guess, int k); -void matrix_add_matrix(matrix from, matrix to); -void scale_matrix(matrix m, float scale); matrix resize_matrix(matrix m, int size); float *pop_column(matrix *m, int c); diff --git a/image.darknet/src/maxpool_layer.c b/image.darknet/src/maxpool_layer.c index 031d116..fb05635 100644 --- a/image.darknet/src/maxpool_layer.c +++ b/image.darknet/src/maxpool_layer.c @@ -27,8 +27,8 @@ maxpool_layer make_maxpool_layer(int batch, int h, int w, int c, int size, int s l.w = w; l.c = c; l.pad = padding; - l.out_w = (w + 2*padding)/stride; - l.out_h = (h + 2*padding)/stride; + l.out_w = (w + padding - size)/stride + 1; + l.out_h = (h + padding - size)/stride + 1; l.out_c = c; l.outputs = l.out_h * l.out_w * l.out_c; l.inputs = h*w*c; @@ -43,7 +43,7 @@ maxpool_layer make_maxpool_layer(int batch, int h, int w, int c, int size, int s #ifdef GPU l.forward_gpu = forward_maxpool_layer_gpu; l.backward_gpu = backward_maxpool_layer_gpu; - l.indexes_gpu = cuda_make_int_array(output_size); + l.indexes_gpu = cuda_make_int_array(0, output_size); l.output_gpu = cuda_make_array(l.output, output_size); l.delta_gpu = cuda_make_array(l.delta, output_size); #endif @@ -57,8 +57,8 @@ void resize_maxpool_layer(maxpool_layer *l, int w, int h) l->w = w; l->inputs = h*w*l->c; - l->out_w = (w + 2*l->pad)/l->stride; - l->out_h = (h + 2*l->pad)/l->stride; + l->out_w = (w + l->pad - l->size)/l->stride + 1; + l->out_h = (h + l->pad - l->size)/l->stride + 1; l->outputs = l->out_w * l->out_h * l->c; int output_size = l->outputs * l->batch; @@ -70,17 +70,17 @@ void resize_maxpool_layer(maxpool_layer *l, int w, int h) cuda_free((float *)l->indexes_gpu); cuda_free(l->output_gpu); cuda_free(l->delta_gpu); - l->indexes_gpu = cuda_make_int_array(output_size); + l->indexes_gpu = cuda_make_int_array(0, output_size); l->output_gpu = cuda_make_array(l->output, output_size); l->delta_gpu = cuda_make_array(l->delta, output_size); #endif } -void forward_maxpool_layer(const maxpool_layer l, network_state state) +void forward_maxpool_layer(const maxpool_layer l, network net) { int b,i,j,k,m,n; - int w_offset = -l.pad; - int h_offset = -l.pad; + int w_offset = -l.pad/2; + int h_offset = -l.pad/2; int h = l.out_h; int w = l.out_w; @@ -100,7 +100,7 @@ void forward_maxpool_layer(const maxpool_layer l, network_state state) int index = cur_w + l.w*(cur_h + l.h*(k + b*l.c)); int valid = (cur_h >= 0 && cur_h < l.h && cur_w >= 0 && cur_w < l.w); - float val = (valid != 0) ? state.input[index] : -FLT_MAX; + float val = (valid != 0) ? net.input[index] : -FLT_MAX; max_i = (val > max) ? index : max_i; max = (val > max) ? val : max; } @@ -113,7 +113,7 @@ void forward_maxpool_layer(const maxpool_layer l, network_state state) } } -void backward_maxpool_layer(const maxpool_layer l, network_state state) +void backward_maxpool_layer(const maxpool_layer l, network net) { int i; int h = l.out_h; @@ -121,7 +121,7 @@ void backward_maxpool_layer(const maxpool_layer l, network_state state) int c = l.c; for(i = 0; i < h*w*c*l.batch; ++i){ int index = l.indexes[i]; - state.delta[index] += l.delta[i]; + net.delta[index] += l.delta[i]; } } diff --git a/image.darknet/src/maxpool_layer.h b/image.darknet/src/maxpool_layer.h index ce56dd8..ceb5190 100644 --- a/image.darknet/src/maxpool_layer.h +++ b/image.darknet/src/maxpool_layer.h @@ -11,12 +11,12 @@ typedef layer maxpool_layer; image get_maxpool_image(maxpool_layer l); maxpool_layer make_maxpool_layer(int batch, int h, int w, int c, int size, int stride, int padding); void resize_maxpool_layer(maxpool_layer *l, int w, int h); -void forward_maxpool_layer(const maxpool_layer l, network_state state); -void backward_maxpool_layer(const maxpool_layer l, network_state state); +void forward_maxpool_layer(const maxpool_layer l, network net); +void backward_maxpool_layer(const maxpool_layer l, network net); #ifdef GPU -void forward_maxpool_layer_gpu(maxpool_layer l, network_state state); -void backward_maxpool_layer_gpu(maxpool_layer l, network_state state); +void forward_maxpool_layer_gpu(maxpool_layer l, network net); +void backward_maxpool_layer_gpu(maxpool_layer l, network net); #endif #endif diff --git a/image.darknet/src/maxpool_layer_kernels.cu b/image.darknet/src/maxpool_layer_kernels.cu index 6381cc1..869ef46 100644 --- a/image.darknet/src/maxpool_layer_kernels.cu +++ b/image.darknet/src/maxpool_layer_kernels.cu @@ -9,8 +9,8 @@ extern "C" { __global__ void forward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_c, int stride, int size, int pad, float *input, float *output, int *indexes) { - int h = (in_h + 2*pad)/stride; - int w = (in_w + 2*pad)/stride; + int h = (in_h + pad - size)/stride + 1; + int w = (in_w + pad - size)/stride + 1; int c = in_c; int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; @@ -24,8 +24,8 @@ __global__ void forward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_c id /= c; int b = id; - int w_offset = -pad; - int h_offset = -pad; + int w_offset = -pad/2; + int h_offset = -pad/2; int out_index = j + w*(i + h*(k + c*b)); float max = -INFINITY; @@ -49,8 +49,8 @@ __global__ void forward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_c __global__ void backward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_c, int stride, int size, int pad, float *delta, float *prev_delta, int *indexes) { - int h = (in_h + 2*pad)/stride; - int w = (in_w + 2*pad)/stride; + int h = (in_h + pad - size)/stride + 1; + int w = (in_w + pad - size)/stride + 1; int c = in_c; int area = (size-1)/stride; @@ -66,8 +66,8 @@ __global__ void backward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_ id /= in_c; int b = id; - int w_offset = -pad; - int h_offset = -pad; + int w_offset = -pad/2; + int h_offset = -pad/2; float d = 0; int l, m; @@ -84,7 +84,7 @@ __global__ void backward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_ prev_delta[index] += d; } -extern "C" void forward_maxpool_layer_gpu(maxpool_layer layer, network_state state) +extern "C" void forward_maxpool_layer_gpu(maxpool_layer layer, network net) { int h = layer.out_h; int w = layer.out_w; @@ -92,15 +92,15 @@ extern "C" void forward_maxpool_layer_gpu(maxpool_layer layer, network_state sta size_t n = h*w*c*layer.batch; - forward_maxpool_layer_kernel<<>>(n, layer.h, layer.w, layer.c, layer.stride, layer.size, layer.pad, state.input, layer.output_gpu, layer.indexes_gpu); + forward_maxpool_layer_kernel<<>>(n, layer.h, layer.w, layer.c, layer.stride, layer.size, layer.pad, net.input_gpu, layer.output_gpu, layer.indexes_gpu); check_error(cudaPeekAtLastError()); } -extern "C" void backward_maxpool_layer_gpu(maxpool_layer layer, network_state state) +extern "C" void backward_maxpool_layer_gpu(maxpool_layer layer, network net) { size_t n = layer.h*layer.w*layer.c*layer.batch; - backward_maxpool_layer_kernel<<>>(n, layer.h, layer.w, layer.c, layer.stride, layer.size, layer.pad, layer.delta_gpu, state.delta, layer.indexes_gpu); + backward_maxpool_layer_kernel<<>>(n, layer.h, layer.w, layer.c, layer.stride, layer.size, layer.pad, layer.delta_gpu, net.delta_gpu, layer.indexes_gpu); check_error(cudaPeekAtLastError()); } diff --git a/image.darknet/src/network.c b/image.darknet/src/network.c index 0914e37..aaab799 100644 --- a/image.darknet/src/network.c +++ b/image.darknet/src/network.c @@ -17,6 +17,7 @@ #include "activation_layer.h" #include "detection_layer.h" #include "region_layer.h" +#include "yolo_layer.h" #include "normalization_layer.h" #include "batchnorm_layer.h" #include "maxpool_layer.h" @@ -26,55 +27,95 @@ #include "softmax_layer.h" #include "dropout_layer.h" #include "route_layer.h" +#include "upsample_layer.h" #include "shortcut_layer.h" +#include "parser.h" +#include "data.h" + +load_args get_base_args(network *net) +{ + load_args args = {0}; + args.w = net->w; + args.h = net->h; + args.size = net->w; + + args.min = net->min_crop; + args.max = net->max_crop; + args.angle = net->angle; + args.aspect = net->aspect; + args.exposure = net->exposure; + args.center = net->center; + args.saturation = net->saturation; + args.hue = net->hue; + return args; +} + +network *load_network(char *cfg, char *weights, int clear) +{ + network *net = parse_network_cfg(cfg); + if(weights && weights[0] != 0){ + load_weights(net, weights); + } + if(clear) (*net->seen) = 0; + return net; +} -int get_current_batch(network net) +size_t get_current_batch(network *net) { - int batch_num = (*net.seen)/(net.batch*net.subdivisions); + size_t batch_num = (*net->seen)/(net->batch*net->subdivisions); return batch_num; } -void reset_momentum(network net) +void reset_network_state(network *net, int b) { - if (net.momentum == 0) return; - net.learning_rate = 0; - net.momentum = 0; - net.decay = 0; - #ifdef GPU - //if(net.gpu_index >= 0) update_network_gpu(net); - #endif + int i; + for (i = 0; i < net->n; ++i) { + #ifdef GPU + layer l = net->layers[i]; + if(l.state_gpu){ + fill_gpu(l.outputs, 0, l.state_gpu + l.outputs*b, 1); + } + if(l.h_gpu){ + fill_gpu(l.outputs, 0, l.h_gpu + l.outputs*b, 1); + } + #endif + } } -float get_current_rate(network net) +void reset_rnn(network *net) { - int batch_num = get_current_batch(net); + reset_network_state(net, 0); +} + +float get_current_rate(network *net) +{ + size_t batch_num = get_current_batch(net); int i; float rate; - switch (net.policy) { + if (batch_num < net->burn_in) return net->learning_rate * pow((float)batch_num / net->burn_in, net->power); + switch (net->policy) { case CONSTANT: - return net.learning_rate; + return net->learning_rate; case STEP: - return net.learning_rate * pow(net.scale, batch_num/net.step); + return net->learning_rate * pow(net->scale, batch_num/net->step); case STEPS: - rate = net.learning_rate; - for(i = 0; i < net.num_steps; ++i){ - if(net.steps[i] > batch_num) return rate; - rate *= net.scales[i]; - //if(net.steps[i] > batch_num - 1 && net.scales[i] > 1) reset_momentum(net); + rate = net->learning_rate; + for(i = 0; i < net->num_steps; ++i){ + if(net->steps[i] > batch_num) return rate; + rate *= net->scales[i]; } return rate; case EXP: - return net.learning_rate * pow(net.gamma, batch_num); + return net->learning_rate * pow(net->gamma, batch_num); case POLY: - if (batch_num < net.burn_in) return net.learning_rate * pow((float)batch_num / net.burn_in, net.power); - return net.learning_rate * pow(1 - (float)batch_num / net.max_batches, net.power); + return net->learning_rate * pow(1 - (float)batch_num / net->max_batches, net->power); case RANDOM: - return net.learning_rate * pow(rand_uniform(0,1), net.power); + return net->learning_rate * pow(rand_uniform(0,1), net->power); case SIG: - return net.learning_rate * (1./(1.+exp(net.gamma*(batch_num - net.step)))); + return net->learning_rate * (1./(1.+exp(net->gamma*(batch_num - net->step)))); default: fprintf(stderr, "Policy is weird!\n"); - return net.learning_rate; + return net->learning_rate; } } @@ -95,6 +136,8 @@ char *get_layer_string(LAYER_TYPE a) return "rnn"; case GRU: return "gru"; + case LSTM: + return "lstm"; case CRNN: return "crnn"; case MAXPOOL: @@ -109,6 +152,8 @@ char *get_layer_string(LAYER_TYPE a) return "detection"; case REGION: return "region"; + case YOLO: + return "yolo"; case DROPOUT: return "dropout"; case CROP: @@ -129,59 +174,75 @@ char *get_layer_string(LAYER_TYPE a) return "none"; } -network make_network(int n) +network *make_network(int n) { - network net = {0}; - net.n = n; - net.layers = calloc(net.n, sizeof(layer)); - net.seen = calloc(1, sizeof(int)); - #ifdef GPU - net.input_gpu = calloc(1, sizeof(float *)); - net.truth_gpu = calloc(1, sizeof(float *)); - #endif + network *net = calloc(1, sizeof(network)); + net->n = n; + net->layers = calloc(net->n, sizeof(layer)); + net->seen = calloc(1, sizeof(size_t)); + net->t = calloc(1, sizeof(int)); + net->cost = calloc(1, sizeof(float)); return net; } -void forward_network(network net, network_state state) +void forward_network(network *netp) { - state.workspace = net.workspace; +#ifdef GPU + if(netp->gpu_index >= 0){ + forward_network_gpu(netp); + return; + } +#endif + network net = *netp; int i; for(i = 0; i < net.n; ++i){ - state.index = i; + net.index = i; layer l = net.layers[i]; if(l.delta){ - scal_cpu(l.outputs * l.batch, 0, l.delta, 1); + fill_cpu(l.outputs * l.batch, 0, l.delta, 1); + } + l.forward(l, net); + net.input = l.output; + if(l.truth) { + net.truth = l.output; } - l.forward(l, state); - state.input = l.output; } + calc_network_cost(netp); } -void update_network(network net) +void update_network(network *netp) { +#ifdef GPU + if(netp->gpu_index >= 0){ + update_network_gpu(netp); + return; + } +#endif + network net = *netp; int i; - int update_batch = net.batch*net.subdivisions; - float rate = get_current_rate(net); + update_args a = {0}; + a.batch = net.batch*net.subdivisions; + a.learning_rate = get_current_rate(netp); + a.momentum = net.momentum; + a.decay = net.decay; + a.adam = net.adam; + a.B1 = net.B1; + a.B2 = net.B2; + a.eps = net.eps; + ++*net.t; + a.t = *net.t; + for(i = 0; i < net.n; ++i){ layer l = net.layers[i]; if(l.update){ - l.update(l, update_batch, rate, net.momentum, net.decay); + l.update(l, a); } } } -float *get_network_output(network net) -{ -#ifdef GPU - if (gpu_index >= 0) return get_network_output_gpu(net); -#endif - int i; - for(i = net.n-1; i > 0; --i) if(net.layers[i].type != COST) break; - return net.layers[i].output; -} - -float get_network_cost(network net) +void calc_network_cost(network *netp) { + network net = *netp; int i; float sum = 0; int count = 0; @@ -191,120 +252,90 @@ float get_network_cost(network net) ++count; } } - return sum/count; + *net.cost = sum/count; } -int get_predicted_class_network(network net) +int get_predicted_class_network(network *net) { - float *out = get_network_output(net); - int k = get_network_output_size(net); - return max_index(out, k); + return max_index(net->output, net->outputs); } -void backward_network(network net, network_state state) +void backward_network(network *netp) { +#ifdef GPU + if(netp->gpu_index >= 0){ + backward_network_gpu(netp); + return; + } +#endif + network net = *netp; int i; - float *original_input = state.input; - float *original_delta = state.delta; - state.workspace = net.workspace; + network orig = net; for(i = net.n-1; i >= 0; --i){ - state.index = i; + layer l = net.layers[i]; + if(l.stopbackward) break; if(i == 0){ - state.input = original_input; - state.delta = original_delta; + net = orig; }else{ layer prev = net.layers[i-1]; - state.input = prev.output; - state.delta = prev.delta; + net.input = prev.output; + net.delta = prev.delta; } - layer l = net.layers[i]; - l.backward(l, state); + net.index = i; + l.backward(l, net); } } -float train_network_datum(network net, float *x, float *y) +float train_network_datum(network *net) { -#ifdef GPU - if(gpu_index >= 0) return train_network_datum_gpu(net, x, y); -#endif - network_state state; - *net.seen += net.batch; - state.index = 0; - state.net = net; - state.input = x; - state.delta = 0; - state.truth = y; - state.train = 1; - forward_network(net, state); - backward_network(net, state); - float error = get_network_cost(net); - if(((*net.seen)/net.batch)%net.subdivisions == 0) update_network(net); + *net->seen += net->batch; + net->train = 1; + forward_network(net); + backward_network(net); + float error = *net->cost; + if(((*net->seen)/net->batch)%net->subdivisions == 0) update_network(net); return error; } -float train_network_sgd(network net, data d, int n) +float train_network_sgd(network *net, data d, int n) { - int batch = net.batch; - float *X = calloc(batch*d.X.cols, sizeof(float)); - float *y = calloc(batch*d.y.cols, sizeof(float)); + int batch = net->batch; int i; float sum = 0; for(i = 0; i < n; ++i){ - get_random_batch(d, batch, X, y); - float err = train_network_datum(net, X, y); + get_random_batch(d, batch, net->input, net->truth); + float err = train_network_datum(net); sum += err; } - free(X); - free(y); return (float)sum/(n*batch); } -float train_network(network net, data d) +float train_network(network *net, data d) { - assert(d.X.rows % net.batch == 0); - int batch = net.batch; + assert(d.X.rows % net->batch == 0); + int batch = net->batch; int n = d.X.rows / batch; - float *X = calloc(batch*d.X.cols, sizeof(float)); - float *y = calloc(batch*d.y.cols, sizeof(float)); int i; float sum = 0; for(i = 0; i < n; ++i){ - get_next_batch(d, batch, i*batch, X, y); - float err = train_network_datum(net, X, y); + get_next_batch(d, batch, i*batch, net->input, net->truth); + float err = train_network_datum(net); sum += err; } - free(X); - free(y); return (float)sum/(n*batch); } - -float train_network_batch(network net, data d, int n) +void set_temp_network(network *net, float t) { - int i,j; - network_state state; - state.index = 0; - state.net = net; - state.train = 1; - state.delta = 0; - float sum = 0; - int batch = 2; - for(i = 0; i < n; ++i){ - for(j = 0; j < batch; ++j){ - int index = rand()%d.X.rows; - state.input = d.X.vals[index]; - state.truth = d.y.vals[index]; - forward_network(net, state); - backward_network(net, state); - sum += get_network_cost(net); - } - update_network(net); + int i; + for(i = 0; i < net->n; ++i){ + net->layers[i].temperature = t; } - return (float)sum/(n*batch); } + void set_batch_network(network *net, int b) { net->batch = b; @@ -315,6 +346,11 @@ void set_batch_network(network *net, int b) if(net->layers[i].type == CONVOLUTIONAL){ cudnn_convolutional_setup(net->layers + i); } + if(net->layers[i].type == DECONVOLUTIONAL){ + layer *l = net->layers + i; + cudnnSetTensor4dDescriptor(l->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l->out_c, l->out_h, l->out_w); + cudnnSetTensor4dDescriptor(l->normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l->out_c, 1, 1); + } #endif } } @@ -323,9 +359,7 @@ int resize_network(network *net, int w, int h) { #ifdef GPU cuda_set_device(net->gpu_index); - if(gpu_index >= 0){ - cuda_free(net->workspace); - } + cuda_free(net->workspace); #endif int i; //if(w == net->w && h == net->h) return 0; @@ -345,8 +379,14 @@ int resize_network(network *net, int w, int h) resize_maxpool_layer(&l, w, h); }else if(l.type == REGION){ resize_region_layer(&l, w, h); + }else if(l.type == YOLO){ + resize_yolo_layer(&l, w, h); }else if(l.type == ROUTE){ resize_route_layer(&l, net); + }else if(l.type == SHORTCUT){ + resize_shortcut_layer(&l, w, h); + }else if(l.type == UPSAMPLE){ + resize_upsample_layer(&l, w, h); }else if(l.type == REORG){ resize_reorg_layer(&l, w, h); }else if(l.type == AVGPOOL){ @@ -359,21 +399,32 @@ int resize_network(network *net, int w, int h) error("Cannot resize this type of layer"); } if(l.workspace_size > workspace_size) workspace_size = l.workspace_size; + if(l.workspace_size > 2000000000) assert(0); inputs = l.outputs; net->layers[i] = l; w = l.out_w; h = l.out_h; if(l.type == AVGPOOL) break; } + layer out = get_network_output_layer(net); + net->inputs = net->layers[0].inputs; + net->outputs = out.outputs; + net->truths = out.outputs; + if(net->layers[net->n-1].truths) net->truths = net->layers[net->n-1].truths; + net->output = out.output; + free(net->input); + free(net->truth); + net->input = calloc(net->inputs*net->batch, sizeof(float)); + net->truth = calloc(net->truths*net->batch, sizeof(float)); #ifdef GPU if(gpu_index >= 0){ - if(net->input_gpu) { - cuda_free(*net->input_gpu); - *net->input_gpu = 0; - cuda_free(*net->truth_gpu); - *net->truth_gpu = 0; + cuda_free(net->input_gpu); + cuda_free(net->truth_gpu); + net->input_gpu = cuda_make_array(net->input, net->inputs*net->batch); + net->truth_gpu = cuda_make_array(net->truth, net->truths*net->batch); + if(workspace_size){ + net->workspace = cuda_make_array(0, (workspace_size-1)/sizeof(float)+1); } - net->workspace = cuda_make_array(0, (workspace_size-1)/sizeof(float)+1); }else { free(net->workspace); net->workspace = calloc(1, workspace_size); @@ -386,34 +437,25 @@ int resize_network(network *net, int w, int h) return 0; } -int get_network_output_size(network net) +layer get_network_detection_layer(network *net) { int i; - for(i = net.n-1; i > 0; --i) if(net.layers[i].type != COST) break; - return net.layers[i].outputs; -} - -int get_network_input_size(network net) -{ - return net.layers[0].inputs; -} - -detection_layer get_network_detection_layer(network net) -{ - int i; - for(i = 0; i < net.n; ++i){ - if(net.layers[i].type == DETECTION){ - return net.layers[i]; + for(i = 0; i < net->n; ++i){ + if(net->layers[i].type == DETECTION){ + return net->layers[i]; } } fprintf(stderr, "Detection layer not found!!\n"); - detection_layer l = {0}; + layer l = {0}; return l; } -image get_network_image_layer(network net, int i) +image get_network_image_layer(network *net, int i) { - layer l = net.layers[i]; + layer l = net->layers[i]; +#ifdef GPU + //cuda_pull_array(l.output_gpu, l.output, l.outputs); +#endif if (l.out_w && l.out_h && l.out_c){ return float_to_image(l.out_w, l.out_h, l.out_c, l.output); } @@ -421,10 +463,10 @@ image get_network_image_layer(network net, int i) return def; } -image get_network_image(network net) +image get_network_image(network *net) { int i; - for(i = net.n-1; i >= 0; --i){ + for(i = net->n-1; i >= 0; --i){ image m = get_network_image_layer(net, i); if(m.h != 0) return m; } @@ -432,60 +474,134 @@ image get_network_image(network net) return def; } -void visualize_network(network net) +void visualize_network(network *net) { image *prev = 0; int i; char buff[256]; - for(i = 0; i < net.n; ++i){ + for(i = 0; i < net->n; ++i){ sprintf(buff, "Layer %d", i); - layer l = net.layers[i]; + layer l = net->layers[i]; if(l.type == CONVOLUTIONAL){ prev = visualize_convolutional_layer(l, buff, prev); } } } -void top_predictions(network net, int k, int *index) +void top_predictions(network *net, int k, int *index) { - int size = get_network_output_size(net); - float *out = get_network_output(net); - top_k(out, size, k, index); + top_k(net->output, net->outputs, k, index); } -float *network_predict(network net, float *input) +float *network_predict(network *net, float *input) { -#ifdef GPU - if(gpu_index >= 0) return network_predict_gpu(net, input); -#endif - - network_state state; - state.net = net; - state.index = 0; - state.input = input; - state.truth = 0; - state.train = 0; - state.delta = 0; - forward_network(net, state); - float *out = get_network_output(net); + network orig = *net; + net->input = input; + net->truth = 0; + net->train = 0; + net->delta = 0; + forward_network(net); + float *out = net->output; + *net = orig; return out; } -matrix network_predict_data_multi(network net, data test, int n) +int num_detections(network *net, float thresh) +{ + int i; + int s = 0; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; + if(l.type == YOLO){ + s += yolo_num_detections(l, thresh); + } + if(l.type == DETECTION || l.type == REGION){ + s += l.w*l.h*l.n; + } + } + return s; +} + +detection *make_network_boxes(network *net, float thresh, int *num) +{ + layer l = net->layers[net->n - 1]; + int i; + int nboxes = num_detections(net, thresh); + if(num) *num = nboxes; + detection *dets = calloc(nboxes, sizeof(detection)); + for(i = 0; i < nboxes; ++i){ + dets[i].prob = calloc(l.classes, sizeof(float)); + if(l.coords > 4){ + dets[i].mask = calloc(l.coords-4, sizeof(float)); + } + } + return dets; +} + +void fill_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, detection *dets) +{ + int j; + for(j = 0; j < net->n; ++j){ + layer l = net->layers[j]; + if(l.type == YOLO){ + int count = get_yolo_detections(l, w, h, net->w, net->h, thresh, map, relative, dets); + dets += count; + } + if(l.type == REGION){ + get_region_detections(l, w, h, net->w, net->h, thresh, map, hier, relative, dets); + dets += l.w*l.h*l.n; + } + if(l.type == DETECTION){ + get_detection_detections(l, w, h, thresh, dets); + dets += l.w*l.h*l.n; + } + } +} + +detection *get_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, int *num) +{ + detection *dets = make_network_boxes(net, thresh, num); + fill_network_boxes(net, w, h, thresh, hier, map, relative, dets); + return dets; +} + +void free_detections(detection *dets, int n) +{ + int i; + for(i = 0; i < n; ++i){ + free(dets[i].prob); + if(dets[i].mask) free(dets[i].mask); + } + free(dets); +} + +float *network_predict_image(network *net, image im) +{ + image imr = letterbox_image(im, net->w, net->h); + set_batch_network(net, 1); + float *p = network_predict(net, imr.data); + free_image(imr); + return p; +} + +int network_width(network *net){return net->w;} +int network_height(network *net){return net->h;} + +matrix network_predict_data_multi(network *net, data test, int n) { int i,j,b,m; - int k = get_network_output_size(net); + int k = net->outputs; matrix pred = make_matrix(test.X.rows, k); - float *X = calloc(net.batch*test.X.rows, sizeof(float)); - for(i = 0; i < test.X.rows; i += net.batch){ - for(b = 0; b < net.batch; ++b){ + float *X = calloc(net->batch*test.X.rows, sizeof(float)); + for(i = 0; i < test.X.rows; i += net->batch){ + for(b = 0; b < net->batch; ++b){ if(i+b == test.X.rows) break; memcpy(X+b*test.X.cols, test.X.vals[i+b], test.X.cols*sizeof(float)); } for(m = 0; m < n; ++m){ float *out = network_predict(net, X); - for(b = 0; b < net.batch; ++b){ + for(b = 0; b < net->batch; ++b){ if(i+b == test.X.rows) break; for(j = 0; j < k; ++j){ pred.vals[i+b][j] += out[j+b*k]/n; @@ -497,19 +613,19 @@ matrix network_predict_data_multi(network net, data test, int n) return pred; } -matrix network_predict_data(network net, data test) +matrix network_predict_data(network *net, data test) { int i,j,b; - int k = get_network_output_size(net); + int k = net->outputs; matrix pred = make_matrix(test.X.rows, k); - float *X = calloc(net.batch*test.X.cols, sizeof(float)); - for(i = 0; i < test.X.rows; i += net.batch){ - for(b = 0; b < net.batch; ++b){ + float *X = calloc(net->batch*test.X.cols, sizeof(float)); + for(i = 0; i < test.X.rows; i += net->batch){ + for(b = 0; b < net->batch; ++b){ if(i+b == test.X.rows) break; memcpy(X+b*test.X.cols, test.X.vals[i+b], test.X.cols*sizeof(float)); } float *out = network_predict(net, X); - for(b = 0; b < net.batch; ++b){ + for(b = 0; b < net->batch; ++b){ if(i+b == test.X.rows) break; for(j = 0; j < k; ++j){ pred.vals[i+b][j] = out[j+b*k]; @@ -520,11 +636,11 @@ matrix network_predict_data(network net, data test) return pred; } -void print_network(network net) +void print_network(network *net) { int i,j; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; float *output = l.output; int n = l.outputs; float mean = mean_array(output, n); @@ -537,7 +653,7 @@ void print_network(network net) } } -void compare_networks(network n1, network n2, data test) +void compare_networks(network *n1, network *n2, data test) { matrix g1 = network_predict_data(n1, test); matrix g2 = network_predict_data(n2, test); @@ -562,7 +678,7 @@ void compare_networks(network n1, network n2, data test) printf("%f\n", num/den); } -float network_accuracy(network net, data d) +float network_accuracy(network *net, data d) { matrix guess = network_predict_data(net, d); float acc = matrix_topk_accuracy(d.y, guess,1); @@ -570,7 +686,7 @@ float network_accuracy(network net, data d) return acc; } -float *network_accuracies(network net, data d, int n) +float *network_accuracies(network *net, data d, int n) { static float acc[2]; matrix guess = network_predict_data(net, d); @@ -580,7 +696,16 @@ float *network_accuracies(network net, data d, int n) return acc; } -float network_accuracy_multi(network net, data d, int n) +layer get_network_output_layer(network *net) +{ + int i; + for(i = net->n - 1; i >= 0; --i){ + if(net->layers[i].type != COST) break; + } + return net->layers[i]; +} + +float network_accuracy_multi(network *net, data d, int n) { matrix guess = network_predict_data_multi(net, d, n); float acc = matrix_topk_accuracy(d.y, guess,1); @@ -588,17 +713,417 @@ float network_accuracy_multi(network net, data d, int n) return acc; } -void free_network(network net) +void free_network(network *net) { int i; - for(i = 0; i < net.n; ++i){ - free_layer(net.layers[i]); + for(i = 0; i < net->n; ++i){ + free_layer(net->layers[i]); } - free(net.layers); + free(net->layers); + if(net->input) free(net->input); + if(net->truth) free(net->truth); #ifdef GPU - if(*net.input_gpu) cuda_free(*net.input_gpu); - if(*net.truth_gpu) cuda_free(*net.truth_gpu); - if(net.input_gpu) free(net.input_gpu); - if(net.truth_gpu) free(net.truth_gpu); + if(net->input_gpu) cuda_free(net->input_gpu); + if(net->truth_gpu) cuda_free(net->truth_gpu); #endif + free(net); +} + +// Some day... +// ^ What the hell is this comment for? + + +layer network_output_layer(network *net) +{ + int i; + for(i = net->n - 1; i >= 0; --i){ + if(net->layers[i].type != COST) break; + } + return net->layers[i]; } + +int network_inputs(network *net) +{ + return net->layers[0].inputs; +} + +int network_outputs(network *net) +{ + return network_output_layer(net).outputs; +} + +float *network_output(network *net) +{ + return network_output_layer(net).output; +} + +#ifdef GPU + +void forward_network_gpu(network *netp) +{ + network net = *netp; + cuda_set_device(net.gpu_index); + cuda_push_array(net.input_gpu, net.input, net.inputs*net.batch); + if(net.truth){ + cuda_push_array(net.truth_gpu, net.truth, net.truths*net.batch); + } + + int i; + for(i = 0; i < net.n; ++i){ + net.index = i; + layer l = net.layers[i]; + if(l.delta_gpu){ + fill_gpu(l.outputs * l.batch, 0, l.delta_gpu, 1); + } + l.forward_gpu(l, net); + net.input_gpu = l.output_gpu; + net.input = l.output; + if(l.truth) { + net.truth_gpu = l.output_gpu; + net.truth = l.output; + } + } + pull_network_output(netp); + calc_network_cost(netp); +} + +void backward_network_gpu(network *netp) +{ + int i; + network net = *netp; + network orig = net; + cuda_set_device(net.gpu_index); + for(i = net.n-1; i >= 0; --i){ + layer l = net.layers[i]; + if(l.stopbackward) break; + if(i == 0){ + net = orig; + }else{ + layer prev = net.layers[i-1]; + net.input = prev.output; + net.delta = prev.delta; + net.input_gpu = prev.output_gpu; + net.delta_gpu = prev.delta_gpu; + } + net.index = i; + l.backward_gpu(l, net); + } +} + +void update_network_gpu(network *netp) +{ + network net = *netp; + cuda_set_device(net.gpu_index); + int i; + update_args a = {0}; + a.batch = net.batch*net.subdivisions; + a.learning_rate = get_current_rate(netp); + a.momentum = net.momentum; + a.decay = net.decay; + a.adam = net.adam; + a.B1 = net.B1; + a.B2 = net.B2; + a.eps = net.eps; + ++*net.t; + a.t = (*net.t); + + for(i = 0; i < net.n; ++i){ + layer l = net.layers[i]; + if(l.update_gpu){ + l.update_gpu(l, a); + } + } +} + +void harmless_update_network_gpu(network *netp) +{ + network net = *netp; + cuda_set_device(net.gpu_index); + int i; + for(i = 0; i < net.n; ++i){ + layer l = net.layers[i]; + if(l.weight_updates_gpu) fill_gpu(l.nweights, 0, l.weight_updates_gpu, 1); + if(l.bias_updates_gpu) fill_gpu(l.nbiases, 0, l.bias_updates_gpu, 1); + if(l.scale_updates_gpu) fill_gpu(l.nbiases, 0, l.scale_updates_gpu, 1); + } +} + +typedef struct { + network *net; + data d; + float *err; +} train_args; + +void *train_thread(void *ptr) +{ + train_args args = *(train_args*)ptr; + free(ptr); + cuda_set_device(args.net->gpu_index); + *args.err = train_network(args.net, args.d); + return 0; +} + +pthread_t train_network_in_thread(network *net, data d, float *err) +{ + pthread_t thread; + train_args *ptr = (train_args *)calloc(1, sizeof(train_args)); + ptr->net = net; + ptr->d = d; + ptr->err = err; + if(pthread_create(&thread, 0, train_thread, ptr)) error("Thread creation failed"); + return thread; +} + +void merge_weights(layer l, layer base) +{ + if (l.type == CONVOLUTIONAL) { + axpy_cpu(l.n, 1, l.bias_updates, 1, base.biases, 1); + axpy_cpu(l.nweights, 1, l.weight_updates, 1, base.weights, 1); + if (l.scales) { + axpy_cpu(l.n, 1, l.scale_updates, 1, base.scales, 1); + } + } else if(l.type == CONNECTED) { + axpy_cpu(l.outputs, 1, l.bias_updates, 1, base.biases, 1); + axpy_cpu(l.outputs*l.inputs, 1, l.weight_updates, 1, base.weights, 1); + } +} + +void scale_weights(layer l, float s) +{ + if (l.type == CONVOLUTIONAL) { + scal_cpu(l.n, s, l.biases, 1); + scal_cpu(l.nweights, s, l.weights, 1); + if (l.scales) { + scal_cpu(l.n, s, l.scales, 1); + } + } else if(l.type == CONNECTED) { + scal_cpu(l.outputs, s, l.biases, 1); + scal_cpu(l.outputs*l.inputs, s, l.weights, 1); + } +} + + +void pull_weights(layer l) +{ + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ + cuda_pull_array(l.biases_gpu, l.bias_updates, l.n); + cuda_pull_array(l.weights_gpu, l.weight_updates, l.nweights); + if(l.scales) cuda_pull_array(l.scales_gpu, l.scale_updates, l.n); + } else if(l.type == CONNECTED){ + cuda_pull_array(l.biases_gpu, l.bias_updates, l.outputs); + cuda_pull_array(l.weights_gpu, l.weight_updates, l.outputs*l.inputs); + } +} + +void push_weights(layer l) +{ + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ + cuda_push_array(l.biases_gpu, l.biases, l.n); + cuda_push_array(l.weights_gpu, l.weights, l.nweights); + if(l.scales) cuda_push_array(l.scales_gpu, l.scales, l.n); + } else if(l.type == CONNECTED){ + cuda_push_array(l.biases_gpu, l.biases, l.outputs); + cuda_push_array(l.weights_gpu, l.weights, l.outputs*l.inputs); + } +} + +void distribute_weights(layer l, layer base) +{ + if (l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL) { + cuda_push_array(l.biases_gpu, base.biases, l.n); + cuda_push_array(l.weights_gpu, base.weights, l.nweights); + if (base.scales) cuda_push_array(l.scales_gpu, base.scales, l.n); + } else if (l.type == CONNECTED) { + cuda_push_array(l.biases_gpu, base.biases, l.outputs); + cuda_push_array(l.weights_gpu, base.weights, l.outputs*l.inputs); + } +} + + +/* + + void pull_updates(layer l) + { + if(l.type == CONVOLUTIONAL){ + cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.n); + cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.nweights); + if(l.scale_updates) cuda_pull_array(l.scale_updates_gpu, l.scale_updates, l.n); + } else if(l.type == CONNECTED){ + cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.outputs); + cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.outputs*l.inputs); + } + } + + void push_updates(layer l) + { + if(l.type == CONVOLUTIONAL){ + cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.n); + cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.nweights); + if(l.scale_updates) cuda_push_array(l.scale_updates_gpu, l.scale_updates, l.n); + } else if(l.type == CONNECTED){ + cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.outputs); + cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.outputs*l.inputs); + } + } + + void update_layer(layer l, network net) + { + int update_batch = net.batch*net.subdivisions; + float rate = get_current_rate(net); + l.t = get_current_batch(net); + if(l.update_gpu){ + l.update_gpu(l, update_batch, rate*l.learning_rate_scale, net.momentum, net.decay); + } + } + void merge_updates(layer l, layer base) + { + if (l.type == CONVOLUTIONAL) { + axpy_cpu(l.n, 1, l.bias_updates, 1, base.bias_updates, 1); + axpy_cpu(l.nweights, 1, l.weight_updates, 1, base.weight_updates, 1); + if (l.scale_updates) { + axpy_cpu(l.n, 1, l.scale_updates, 1, base.scale_updates, 1); + } + } else if(l.type == CONNECTED) { + axpy_cpu(l.outputs, 1, l.bias_updates, 1, base.bias_updates, 1); + axpy_cpu(l.outputs*l.inputs, 1, l.weight_updates, 1, base.weight_updates, 1); + } + } + + void distribute_updates(layer l, layer base) + { + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ + cuda_push_array(l.bias_updates_gpu, base.bias_updates, l.n); + cuda_push_array(l.weight_updates_gpu, base.weight_updates, l.nweights); + if(base.scale_updates) cuda_push_array(l.scale_updates_gpu, base.scale_updates, l.n); + } else if(l.type == CONNECTED){ + cuda_push_array(l.bias_updates_gpu, base.bias_updates, l.outputs); + cuda_push_array(l.weight_updates_gpu, base.weight_updates, l.outputs*l.inputs); + } + } + */ + +/* + void sync_layer(network *nets, int n, int j) + { + int i; + network net = nets[0]; + layer base = net.layers[j]; + scale_weights(base, 0); + for (i = 0; i < n; ++i) { + cuda_set_device(nets[i].gpu_index); + layer l = nets[i].layers[j]; + pull_weights(l); + merge_weights(l, base); + } + scale_weights(base, 1./n); + for (i = 0; i < n; ++i) { + cuda_set_device(nets[i].gpu_index); + layer l = nets[i].layers[j]; + distribute_weights(l, base); + } + } + */ + +void sync_layer(network **nets, int n, int j) +{ + int i; + network *net = nets[0]; + layer base = net->layers[j]; + scale_weights(base, 0); + for (i = 0; i < n; ++i) { + cuda_set_device(nets[i]->gpu_index); + layer l = nets[i]->layers[j]; + pull_weights(l); + merge_weights(l, base); + } + scale_weights(base, 1./n); + for (i = 0; i < n; ++i) { + cuda_set_device(nets[i]->gpu_index); + layer l = nets[i]->layers[j]; + distribute_weights(l, base); + } +} + +typedef struct{ + network **nets; + int n; + int j; +} sync_args; + +void *sync_layer_thread(void *ptr) +{ + sync_args args = *(sync_args*)ptr; + sync_layer(args.nets, args.n, args.j); + free(ptr); + return 0; +} + +pthread_t sync_layer_in_thread(network **nets, int n, int j) +{ + pthread_t thread; + sync_args *ptr = (sync_args *)calloc(1, sizeof(sync_args)); + ptr->nets = nets; + ptr->n = n; + ptr->j = j; + if(pthread_create(&thread, 0, sync_layer_thread, ptr)) error("Thread creation failed"); + return thread; +} + +void sync_nets(network **nets, int n, int interval) +{ + int j; + int layers = nets[0]->n; + pthread_t *threads = (pthread_t *) calloc(layers, sizeof(pthread_t)); + + *(nets[0]->seen) += interval * (n-1) * nets[0]->batch * nets[0]->subdivisions; + for (j = 0; j < n; ++j){ + *(nets[j]->seen) = *(nets[0]->seen); + } + for (j = 0; j < layers; ++j) { + threads[j] = sync_layer_in_thread(nets, n, j); + } + for (j = 0; j < layers; ++j) { + pthread_join(threads[j], 0); + } + free(threads); +} + +float train_networks(network **nets, int n, data d, int interval) +{ + int i; + int batch = nets[0]->batch; + int subdivisions = nets[0]->subdivisions; + assert(batch * subdivisions * n == d.X.rows); + pthread_t *threads = (pthread_t *) calloc(n, sizeof(pthread_t)); + float *errors = (float *) calloc(n, sizeof(float)); + + float sum = 0; + for(i = 0; i < n; ++i){ + data p = get_data_part(d, i, n); + threads[i] = train_network_in_thread(nets[i], p, errors + i); + } + for(i = 0; i < n; ++i){ + pthread_join(threads[i], 0); + //printf("%f\n", errors[i]); + sum += errors[i]; + } + //cudaDeviceSynchronize(); + if (get_current_batch(nets[0]) % interval == 0) { + printf("Syncing... "); + fflush(stdout); + sync_nets(nets, n, interval); + printf("Done!\n"); + } + //cudaDeviceSynchronize(); + free(threads); + free(errors); + return (float)sum/(n); +} + +void pull_network_output(network *net) +{ + layer l = get_network_output_layer(net); + cuda_pull_array(l.output_gpu, l.output, l.outputs*l.batch); +} + +#endif diff --git a/image.darknet/src/network.h b/image.darknet/src/network.h index e48cbc2..1b0dfd1 100644 --- a/image.darknet/src/network.h +++ b/image.darknet/src/network.h @@ -1,129 +1,29 @@ // Oh boy, why am I about to do this.... #ifndef NETWORK_H #define NETWORK_H +#include "darknet.h" #include "image.h" #include "layer.h" #include "data.h" #include "tree.h" -typedef enum { - CONSTANT, STEP, EXP, POLY, STEPS, SIG, RANDOM -} learning_rate_policy; - -typedef struct network{ - float *workspace; - int n; - int batch; - int *seen; - float epoch; - int subdivisions; - float momentum; - float decay; - layer *layers; - int outputs; - float *output; - learning_rate_policy policy; - - float learning_rate; - float gamma; - float scale; - float power; - int time_steps; - int step; - int max_batches; - float *scales; - int *steps; - int num_steps; - int burn_in; - - int adam; - float B1; - float B2; - float eps; - - int inputs; - int h, w, c; - int max_crop; - int min_crop; - float angle; - float aspect; - float exposure; - float saturation; - float hue; - - int gpu_index; - tree *hierarchy; - - #ifdef GPU - float **input_gpu; - float **truth_gpu; - #endif -} network; - -typedef struct network_state { - float *truth; - float *input; - float *delta; - float *workspace; - int train; - int index; - network net; -} network_state; #ifdef GPU -float train_networks(network *nets, int n, data d, int interval); -void sync_nets(network *nets, int n, int interval); -float train_network_datum_gpu(network net, float *x, float *y); -float *network_predict_gpu(network net, float *input); -float * get_network_output_gpu_layer(network net, int i); -float * get_network_delta_gpu_layer(network net, int i); -float *get_network_output_gpu(network net); -void forward_network_gpu(network net, network_state state); -void backward_network_gpu(network net, network_state state); -void update_network_gpu(network net); +void pull_network_output(network *net); #endif -float get_current_rate(network net); -int get_current_batch(network net); -void free_network(network net); -void compare_networks(network n1, network n2, data d); +void compare_networks(network *n1, network *n2, data d); char *get_layer_string(LAYER_TYPE a); -network make_network(int n); -void forward_network(network net, network_state state); -void backward_network(network net, network_state state); -void update_network(network net); +network *make_network(int n); -float train_network(network net, data d); -float train_network_batch(network net, data d, int n); -float train_network_sgd(network net, data d, int n); -float train_network_datum(network net, float *x, float *y); -matrix network_predict_data(network net, data test); -float *network_predict(network net, float *input); -float network_accuracy(network net, data d); -float *network_accuracies(network net, data d, int n); -float network_accuracy_multi(network net, data d, int n); -void top_predictions(network net, int n, int *index); -float *get_network_output(network net); -float *get_network_output_layer(network net, int i); -float *get_network_delta_layer(network net, int i); -float *get_network_delta(network net); -int get_network_output_size_layer(network net, int i); -int get_network_output_size(network net); -image get_network_image(network net); -image get_network_image_layer(network net, int i); -int get_predicted_class_network(network net); -void print_network(network net); -void visualize_network(network net); +float network_accuracy_multi(network *net, data d, int n); +int get_predicted_class_network(network *net); +void print_network(network *net); int resize_network(network *net, int w, int h); -void set_batch_network(network *net, int b); -int get_network_input_size(network net); -float get_network_cost(network net); - -int get_network_nuisance(network net); -int get_network_background(network net); +void calc_network_cost(network *net); #endif diff --git a/image.darknet/src/network_kernels.cu b/image.darknet/src/network_kernels.cu deleted file mode 100644 index 313cd6d..0000000 --- a/image.darknet/src/network_kernels.cu +++ /dev/null @@ -1,408 +0,0 @@ -#include "cuda_runtime.h" -#include "curand.h" -#include "cublas_v2.h" - -extern "C" { -#include -#include -#include - -#include "network.h" -#include "image.h" -#include "data.h" -#include "utils.h" -#include "parser.h" - -#include "crop_layer.h" -#include "connected_layer.h" -#include "rnn_layer.h" -#include "gru_layer.h" -#include "crnn_layer.h" -#include "detection_layer.h" -#include "region_layer.h" -#include "convolutional_layer.h" -#include "activation_layer.h" -#include "maxpool_layer.h" -#include "reorg_layer.h" -#include "avgpool_layer.h" -#include "normalization_layer.h" -#include "batchnorm_layer.h" -#include "cost_layer.h" -#include "local_layer.h" -#include "softmax_layer.h" -#include "dropout_layer.h" -#include "route_layer.h" -#include "shortcut_layer.h" -#include "blas.h" -} - -float * get_network_output_gpu_layer(network net, int i); -float * get_network_delta_gpu_layer(network net, int i); -float * get_network_output_gpu(network net); - -void forward_network_gpu(network net, network_state state) -{ - state.workspace = net.workspace; - int i; - for(i = 0; i < net.n; ++i){ - state.index = i; - layer l = net.layers[i]; - if(l.delta_gpu){ - fill_ongpu(l.outputs * l.batch, 0, l.delta_gpu, 1); - } - l.forward_gpu(l, state); - state.input = l.output_gpu; - } -} - -void backward_network_gpu(network net, network_state state) -{ - state.workspace = net.workspace; - int i; - float * original_input = state.input; - float * original_delta = state.delta; - for(i = net.n-1; i >= 0; --i){ - state.index = i; - layer l = net.layers[i]; - if(i == 0){ - state.input = original_input; - state.delta = original_delta; - }else{ - layer prev = net.layers[i-1]; - state.input = prev.output_gpu; - state.delta = prev.delta_gpu; - } - l.backward_gpu(l, state); - } -} - -void update_network_gpu(network net) -{ - cuda_set_device(net.gpu_index); - int i; - int update_batch = net.batch*net.subdivisions; - float rate = get_current_rate(net); - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; - l.t = get_current_batch(net); - if(l.update_gpu){ - l.update_gpu(l, update_batch, rate, net.momentum, net.decay); - } - } -} - -void forward_backward_network_gpu(network net, float *x, float *y) -{ - network_state state; - state.index = 0; - state.net = net; - int x_size = get_network_input_size(net)*net.batch; - int y_size = get_network_output_size(net)*net.batch; - if(net.layers[net.n-1].truths) y_size = net.layers[net.n-1].truths*net.batch; - if(!*net.input_gpu){ - *net.input_gpu = cuda_make_array(x, x_size); - *net.truth_gpu = cuda_make_array(y, y_size); - }else{ - cuda_push_array(*net.input_gpu, x, x_size); - cuda_push_array(*net.truth_gpu, y, y_size); - } - state.input = *net.input_gpu; - state.delta = 0; - state.truth = *net.truth_gpu; - state.train = 1; - forward_network_gpu(net, state); - backward_network_gpu(net, state); -} - -float train_network_datum_gpu(network net, float *x, float *y) -{ - *net.seen += net.batch; - forward_backward_network_gpu(net, x, y); - float error = get_network_cost(net); - if (((*net.seen) / net.batch) % net.subdivisions == 0) update_network_gpu(net); - - return error; -} - -typedef struct { - network net; - data d; - float *err; -} train_args; - -void *train_thread(void *ptr) -{ - train_args args = *(train_args*)ptr; - free(ptr); - cuda_set_device(args.net.gpu_index); - *args.err = train_network(args.net, args.d); - return 0; -} - -pthread_t train_network_in_thread(network net, data d, float *err) -{ - pthread_t thread; - train_args *ptr = (train_args *)calloc(1, sizeof(train_args)); - ptr->net = net; - ptr->d = d; - ptr->err = err; - if(pthread_create(&thread, 0, train_thread, ptr)) error("Thread creation failed"); - return thread; -} - -void pull_updates(layer l) -{ - if(l.type == CONVOLUTIONAL){ - cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.n); - cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.n*l.size*l.size*l.c); - if(l.scale_updates) cuda_pull_array(l.scale_updates_gpu, l.scale_updates, l.n); - } else if(l.type == CONNECTED){ - cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.outputs); - cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.outputs*l.inputs); - } -} - -void push_updates(layer l) -{ - if(l.type == CONVOLUTIONAL){ - cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.n); - cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.n*l.size*l.size*l.c); - if(l.scale_updates) cuda_push_array(l.scale_updates_gpu, l.scale_updates, l.n); - } else if(l.type == CONNECTED){ - cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.outputs); - cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.outputs*l.inputs); - } -} - -void update_layer(layer l, network net) -{ - int update_batch = net.batch*net.subdivisions; - float rate = get_current_rate(net); - l.t = get_current_batch(net); - if(l.update_gpu){ - l.update_gpu(l, update_batch, rate, net.momentum, net.decay); - } -} - -void merge_weights(layer l, layer base) -{ - if (l.type == CONVOLUTIONAL) { - axpy_cpu(l.n, 1, l.biases, 1, base.biases, 1); - axpy_cpu(l.n*l.size*l.size*l.c, 1, l.weights, 1, base.weights, 1); - if (l.scales) { - axpy_cpu(l.n, 1, l.scales, 1, base.scales, 1); - } - } else if(l.type == CONNECTED) { - axpy_cpu(l.outputs, 1, l.biases, 1, base.biases, 1); - axpy_cpu(l.outputs*l.inputs, 1, l.weights, 1, base.weights, 1); - } -} - -void scale_weights(layer l, float s) -{ - if (l.type == CONVOLUTIONAL) { - scal_cpu(l.n, s, l.biases, 1); - scal_cpu(l.n*l.size*l.size*l.c, s, l.weights, 1); - if (l.scales) { - scal_cpu(l.n, s, l.scales, 1); - } - } else if(l.type == CONNECTED) { - scal_cpu(l.outputs, s, l.biases, 1); - scal_cpu(l.outputs*l.inputs, s, l.weights, 1); - } -} - - -void pull_weights(layer l) -{ - if(l.type == CONVOLUTIONAL){ - cuda_pull_array(l.biases_gpu, l.biases, l.n); - cuda_pull_array(l.weights_gpu, l.weights, l.n*l.size*l.size*l.c); - if(l.scales) cuda_pull_array(l.scales_gpu, l.scales, l.n); - } else if(l.type == CONNECTED){ - cuda_pull_array(l.biases_gpu, l.biases, l.outputs); - cuda_pull_array(l.weights_gpu, l.weights, l.outputs*l.inputs); - } -} - -void push_weights(layer l) -{ - if(l.type == CONVOLUTIONAL){ - cuda_push_array(l.biases_gpu, l.biases, l.n); - cuda_push_array(l.weights_gpu, l.weights, l.n*l.size*l.size*l.c); - if(l.scales) cuda_push_array(l.scales_gpu, l.scales, l.n); - } else if(l.type == CONNECTED){ - cuda_push_array(l.biases_gpu, l.biases, l.outputs); - cuda_push_array(l.weights_gpu, l.weights, l.outputs*l.inputs); - } -} - -void distribute_weights(layer l, layer base) -{ - if(l.type == CONVOLUTIONAL){ - cuda_push_array(l.biases_gpu, base.biases, l.n); - cuda_push_array(l.weights_gpu, base.weights, l.n*l.size*l.size*l.c); - if(base.scales) cuda_push_array(l.scales_gpu, base.scales, l.n); - } else if(l.type == CONNECTED){ - cuda_push_array(l.biases_gpu, base.biases, l.outputs); - cuda_push_array(l.weights_gpu, base.weights, l.outputs*l.inputs); - } -} - - -void merge_updates(layer l, layer base) -{ - if (l.type == CONVOLUTIONAL) { - axpy_cpu(l.n, 1, l.bias_updates, 1, base.bias_updates, 1); - axpy_cpu(l.n*l.size*l.size*l.c, 1, l.weight_updates, 1, base.weight_updates, 1); - if (l.scale_updates) { - axpy_cpu(l.n, 1, l.scale_updates, 1, base.scale_updates, 1); - } - } else if(l.type == CONNECTED) { - axpy_cpu(l.outputs, 1, l.bias_updates, 1, base.bias_updates, 1); - axpy_cpu(l.outputs*l.inputs, 1, l.weight_updates, 1, base.weight_updates, 1); - } -} - -void distribute_updates(layer l, layer base) -{ - if(l.type == CONVOLUTIONAL){ - cuda_push_array(l.bias_updates_gpu, base.bias_updates, l.n); - cuda_push_array(l.weight_updates_gpu, base.weight_updates, l.n*l.size*l.size*l.c); - if(base.scale_updates) cuda_push_array(l.scale_updates_gpu, base.scale_updates, l.n); - } else if(l.type == CONNECTED){ - cuda_push_array(l.bias_updates_gpu, base.bias_updates, l.outputs); - cuda_push_array(l.weight_updates_gpu, base.weight_updates, l.outputs*l.inputs); - } -} - -void sync_layer(network *nets, int n, int j) -{ - //printf("Syncing layer %d\n", j); - int i; - network net = nets[0]; - layer base = net.layers[j]; - cuda_set_device(net.gpu_index); - pull_weights(base); - for (i = 1; i < n; ++i) { - cuda_set_device(nets[i].gpu_index); - layer l = nets[i].layers[j]; - pull_weights(l); - merge_weights(l, base); - } - scale_weights(base, 1./n); - for (i = 0; i < n; ++i) { - cuda_set_device(nets[i].gpu_index); - layer l = nets[i].layers[j]; - distribute_weights(l, base); - } - //printf("Done syncing layer %d\n", j); -} - -typedef struct{ - network *nets; - int n; - int j; -} sync_args; - -void *sync_layer_thread(void *ptr) -{ - sync_args args = *(sync_args*)ptr; - sync_layer(args.nets, args.n, args.j); - free(ptr); - return 0; -} - -pthread_t sync_layer_in_thread(network *nets, int n, int j) -{ - pthread_t thread; - sync_args *ptr = (sync_args *)calloc(1, sizeof(sync_args)); - ptr->nets = nets; - ptr->n = n; - ptr->j = j; - if(pthread_create(&thread, 0, sync_layer_thread, ptr)) error("Thread creation failed"); - return thread; -} - -void sync_nets(network *nets, int n, int interval) -{ - int j; - int layers = nets[0].n; - pthread_t *threads = (pthread_t *) calloc(layers, sizeof(pthread_t)); - - *nets[0].seen += interval * (n-1) * nets[0].batch * nets[0].subdivisions; - for (j = 0; j < n; ++j){ - *nets[j].seen = *nets[0].seen; - } - for (j = 0; j < layers; ++j) { - threads[j] = sync_layer_in_thread(nets, n, j); - } - for (j = 0; j < layers; ++j) { - pthread_join(threads[j], 0); - } - free(threads); -} - -float train_networks(network *nets, int n, data d, int interval) -{ - int i; - int batch = nets[0].batch; - int subdivisions = nets[0].subdivisions; - assert(batch * subdivisions * n == d.X.rows); - pthread_t *threads = (pthread_t *) calloc(n, sizeof(pthread_t)); - float *errors = (float *) calloc(n, sizeof(float)); - - float sum = 0; - for(i = 0; i < n; ++i){ - data p = get_data_part(d, i, n); - threads[i] = train_network_in_thread(nets[i], p, errors + i); - } - for(i = 0; i < n; ++i){ - pthread_join(threads[i], 0); - //printf("%f\n", errors[i]); - sum += errors[i]; - } - //cudaDeviceSynchronize(); - if (get_current_batch(nets[0]) % interval == 0) { - printf("Syncing... "); - fflush(stdout); - sync_nets(nets, n, interval); - printf("Done!\n"); - } - //cudaDeviceSynchronize(); - free(threads); - free(errors); - return (float)sum/(n); -} - -float *get_network_output_layer_gpu(network net, int i) -{ - layer l = net.layers[i]; - if(l.type != REGION) cuda_pull_array(l.output_gpu, l.output, l.outputs*l.batch); - return l.output; -} - -float *get_network_output_gpu(network net) -{ - int i; - for(i = net.n-1; i > 0; --i) if(net.layers[i].type != COST) break; - return get_network_output_layer_gpu(net, i); -} - -float *network_predict_gpu(network net, float *input) -{ - cuda_set_device(net.gpu_index); - int size = get_network_input_size(net) * net.batch; - network_state state; - state.index = 0; - state.net = net; - state.input = cuda_make_array(input, size); - state.truth = 0; - state.train = 0; - state.delta = 0; - forward_network_gpu(net, state); - float *out = get_network_output_gpu(net); - cuda_free(state.input); - return out; -} - diff --git a/image.darknet/src/nightmare.c b/image.darknet/src/nightmare.c deleted file mode 100644 index ec7166c..0000000 --- a/image.darknet/src/nightmare.c +++ /dev/null @@ -1,308 +0,0 @@ - -#include "network.h" -#include "parser.h" -#include "blas.h" -#include "utils.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -// ./darknet nightmare cfg/extractor.recon.cfg ~/trained/yolo-coco.conv frame6.png -reconstruct -iters 500 -i 3 -lambda .1 -rate .01 -smooth 2 - -float abs_mean(float *x, int n) -{ - int i; - float sum = 0; - for (i = 0; i < n; ++i){ - sum += fabs(x[i]); - } - return sum/n; -} - -void calculate_loss(float *output, float *delta, int n, float thresh) -{ - int i; - float mean = mean_array(output, n); - float var = variance_array(output, n); - for(i = 0; i < n; ++i){ - if(delta[i] > mean + thresh*sqrt(var)) delta[i] = output[i]; - else delta[i] = 0; - } -} - -void optimize_picture(network *net, image orig, int max_layer, float scale, float rate, float thresh, int norm) -{ - //scale_image(orig, 2); - //translate_image(orig, -1); - net->n = max_layer + 1; - - int dx = rand()%16 - 8; - int dy = rand()%16 - 8; - int flip = rand()%2; - - image crop = crop_image(orig, dx, dy, orig.w, orig.h); - image im = resize_image(crop, (int)(orig.w * scale), (int)(orig.h * scale)); - if(flip) flip_image(im); - - resize_network(net, im.w, im.h); - layer last = net->layers[net->n-1]; - //net->layers[net->n - 1].activation = LINEAR; - - image delta = make_image(im.w, im.h, im.c); - - network_state state = {0}; - -#ifdef GPU - state.input = cuda_make_array(im.data, im.w*im.h*im.c); - state.delta = cuda_make_array(im.data, im.w*im.h*im.c); - - forward_network_gpu(*net, state); - copy_ongpu(last.outputs, last.output_gpu, 1, last.delta_gpu, 1); - - cuda_pull_array(last.delta_gpu, last.delta, last.outputs); - calculate_loss(last.delta, last.delta, last.outputs, thresh); - cuda_push_array(last.delta_gpu, last.delta, last.outputs); - - backward_network_gpu(*net, state); - - cuda_pull_array(state.delta, delta.data, im.w*im.h*im.c); - cuda_free(state.input); - cuda_free(state.delta); -#else - state.input = im.data; - state.delta = delta.data; - forward_network(*net, state); - copy_cpu(last.outputs, last.output, 1, last.delta, 1); - calculate_loss(last.output, last.delta, last.outputs, thresh); - backward_network(*net, state); -#endif - - if(flip) flip_image(delta); - //normalize_array(delta.data, delta.w*delta.h*delta.c); - image resized = resize_image(delta, orig.w, orig.h); - image out = crop_image(resized, -dx, -dy, orig.w, orig.h); - - /* - image g = grayscale_image(out); - free_image(out); - out = g; - */ - - //rate = rate / abs_mean(out.data, out.w*out.h*out.c); - - if(norm) normalize_array(out.data, out.w*out.h*out.c); - axpy_cpu(orig.w*orig.h*orig.c, rate, out.data, 1, orig.data, 1); - - /* - normalize_array(orig.data, orig.w*orig.h*orig.c); - scale_image(orig, sqrt(var)); - translate_image(orig, mean); - */ - - //translate_image(orig, 1); - //scale_image(orig, .5); - //normalize_image(orig); - - constrain_image(orig); - - free_image(crop); - free_image(im); - free_image(delta); - free_image(resized); - free_image(out); - -} - -void smooth(image recon, image update, float lambda, int num) -{ - int i, j, k; - int ii, jj; - for(k = 0; k < recon.c; ++k){ - for(j = 0; j < recon.h; ++j){ - for(i = 0; i < recon.w; ++i){ - int out_index = i + recon.w*(j + recon.h*k); - for(jj = j-num; jj <= j + num && jj < recon.h; ++jj){ - if (jj < 0) continue; - for(ii = i-num; ii <= i + num && ii < recon.w; ++ii){ - if (ii < 0) continue; - int in_index = ii + recon.w*(jj + recon.h*k); - update.data[out_index] += lambda * (recon.data[in_index] - recon.data[out_index]); - } - } - } - } - } -} - -void reconstruct_picture(network net, float *features, image recon, image update, float rate, float momentum, float lambda, int smooth_size, int iters) -{ - int iter = 0; - for (iter = 0; iter < iters; ++iter) { - image delta = make_image(recon.w, recon.h, recon.c); - - network_state state = {0}; -#ifdef GPU - state.input = cuda_make_array(recon.data, recon.w*recon.h*recon.c); - state.delta = cuda_make_array(delta.data, delta.w*delta.h*delta.c); - state.truth = cuda_make_array(features, get_network_output_size(net)); - - forward_network_gpu(net, state); - backward_network_gpu(net, state); - - cuda_pull_array(state.delta, delta.data, delta.w*delta.h*delta.c); - - cuda_free(state.input); - cuda_free(state.delta); - cuda_free(state.truth); -#else - state.input = recon.data; - state.delta = delta.data; - state.truth = features; - - forward_network(net, state); - backward_network(net, state); -#endif - - axpy_cpu(recon.w*recon.h*recon.c, 1, delta.data, 1, update.data, 1); - smooth(recon, update, lambda, smooth_size); - - axpy_cpu(recon.w*recon.h*recon.c, rate, update.data, 1, recon.data, 1); - scal_cpu(recon.w*recon.h*recon.c, momentum, update.data, 1); - - //float mag = mag_array(recon.data, recon.w*recon.h*recon.c); - //scal_cpu(recon.w*recon.h*recon.c, 600/mag, recon.data, 1); - - constrain_image(recon); - free_image(delta); - } -} - - -void run_nightmare(int argc, char **argv) -{ - srand(0); - if(argc < 4){ - fprintf(stderr, "usage: %s %s [cfg] [weights] [image] [layer] [options! (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[2]; - char *weights = argv[3]; - char *input = argv[4]; - int max_layer = atoi(argv[5]); - - int range = find_int_arg(argc, argv, "-range", 1); - int norm = find_int_arg(argc, argv, "-norm", 1); - int rounds = find_int_arg(argc, argv, "-rounds", 1); - int iters = find_int_arg(argc, argv, "-iters", 10); - int octaves = find_int_arg(argc, argv, "-octaves", 4); - float zoom = find_float_arg(argc, argv, "-zoom", 1.); - float rate = find_float_arg(argc, argv, "-rate", .04); - float thresh = find_float_arg(argc, argv, "-thresh", 1.); - float rotate = find_float_arg(argc, argv, "-rotate", 0); - float momentum = find_float_arg(argc, argv, "-momentum", .9); - float lambda = find_float_arg(argc, argv, "-lambda", .01); - char *prefix = find_char_arg(argc, argv, "-prefix", 0); - int reconstruct = find_arg(argc, argv, "-reconstruct"); - int smooth_size = find_int_arg(argc, argv, "-smooth", 1); - - network net = parse_network_cfg(cfg); - load_weights(&net, weights); - char *cfgbase = basecfg(cfg); - char *imbase = basecfg(input); - - set_batch_network(&net, 1); - image im = load_image_color(input, 0, 0); - if(0){ - float scale = 1; - if(im.w > 512 || im.h > 512){ - if(im.w > im.h) scale = 512.0/im.w; - else scale = 512.0/im.h; - } - image resized = resize_image(im, scale*im.w, scale*im.h); - free_image(im); - im = resized; - } - - float *features = 0; - image update; - if (reconstruct){ - resize_network(&net, im.w, im.h); - - int zz = 0; - network_predict(net, im.data); - image out_im = get_network_image(net); - image crop = crop_image(out_im, zz, zz, out_im.w-2*zz, out_im.h-2*zz); - //flip_image(crop); - image f_im = resize_image(crop, out_im.w, out_im.h); - free_image(crop); - printf("%d features\n", out_im.w*out_im.h*out_im.c); - - - im = resize_image(im, im.w, im.h); - f_im = resize_image(f_im, f_im.w, f_im.h); - features = f_im.data; - - int i; - for(i = 0; i < 14*14*512; ++i){ - features[i] += rand_uniform(-.19, .19); - } - - free_image(im); - im = make_random_image(im.w, im.h, im.c); - update = make_image(im.w, im.h, im.c); - - } - - int e; - int n; - for(e = 0; e < rounds; ++e){ - fprintf(stderr, "Iteration: "); - fflush(stderr); - for(n = 0; n < iters; ++n){ - fprintf(stderr, "%d, ", n); - fflush(stderr); - if(reconstruct){ - reconstruct_picture(net, features, im, update, rate, momentum, lambda, smooth_size, 1); - //if ((n+1)%30 == 0) rate *= .5; - show_image(im, "reconstruction"); -#ifdef OPENCV - cvWaitKey(10); -#endif - }else{ - int layer = max_layer + rand()%range - range/2; - int octave = rand()%octaves; - optimize_picture(&net, im, layer, 1/pow(1.33333333, octave), rate, thresh, norm); - } - } - fprintf(stderr, "done\n"); - if(0){ - image g = grayscale_image(im); - free_image(im); - im = g; - } - char buff[256]; - if (prefix){ - sprintf(buff, "%s/%s_%s_%d_%06d",prefix, imbase, cfgbase, max_layer, e); - }else{ - sprintf(buff, "%s_%s_%d_%06d",imbase, cfgbase, max_layer, e); - } - printf("%d %s\n", e, buff); - save_image(im, buff); - //show_image(im, buff); - //cvWaitKey(0); - - if(rotate){ - image rot = rotate_image(im, rotate); - free_image(im); - im = rot; - } - image crop = crop_image(im, im.w * (1. - zoom)/2., im.h * (1.-zoom)/2., im.w*zoom, im.h*zoom); - image resized = resize_image(crop, im.w, im.h); - free_image(im); - free_image(crop); - im = resized; - } -} - diff --git a/image.darknet/src/normalization_layer.c b/image.darknet/src/normalization_layer.c index 069a079..424714f 100644 --- a/image.darknet/src/normalization_layer.c +++ b/image.darknet/src/normalization_layer.c @@ -1,5 +1,6 @@ #include "normalization_layer.h" #include "blas.h" + #include layer make_normalization_layer(int batch, int w, int h, int c, int size, float alpha, float beta, float kappa) @@ -62,7 +63,7 @@ void resize_normalization_layer(layer *layer, int w, int h) #endif } -void forward_normalization_layer(const layer layer, network_state state) +void forward_normalization_layer(const layer layer, network net) { int k,b; int w = layer.w; @@ -73,7 +74,7 @@ void forward_normalization_layer(const layer layer, network_state state) for(b = 0; b < layer.batch; ++b){ float *squared = layer.squared + w*h*c*b; float *norms = layer.norms + w*h*c*b; - float *input = state.input + w*h*c*b; + float *input = net.input + w*h*c*b; pow_cpu(w*h*c, 2, input, 1, squared, 1); const_cpu(w*h, layer.kappa, norms, 1); @@ -90,10 +91,10 @@ void forward_normalization_layer(const layer layer, network_state state) } } pow_cpu(w*h*c*layer.batch, -layer.beta, layer.norms, 1, layer.output, 1); - mul_cpu(w*h*c*layer.batch, state.input, 1, layer.output, 1); + mul_cpu(w*h*c*layer.batch, net.input, 1, layer.output, 1); } -void backward_normalization_layer(const layer layer, network_state state) +void backward_normalization_layer(const layer layer, network net) { // TODO This is approximate ;-) // Also this should add in to delta instead of overwritting. @@ -101,50 +102,50 @@ void backward_normalization_layer(const layer layer, network_state state) int w = layer.w; int h = layer.h; int c = layer.c; - pow_cpu(w*h*c*layer.batch, -layer.beta, layer.norms, 1, state.delta, 1); - mul_cpu(w*h*c*layer.batch, layer.delta, 1, state.delta, 1); + pow_cpu(w*h*c*layer.batch, -layer.beta, layer.norms, 1, net.delta, 1); + mul_cpu(w*h*c*layer.batch, layer.delta, 1, net.delta, 1); } #ifdef GPU -void forward_normalization_layer_gpu(const layer layer, network_state state) +void forward_normalization_layer_gpu(const layer layer, network net) { int k,b; int w = layer.w; int h = layer.h; int c = layer.c; - scal_ongpu(w*h*c*layer.batch, 0, layer.squared_gpu, 1); + scal_gpu(w*h*c*layer.batch, 0, layer.squared_gpu, 1); for(b = 0; b < layer.batch; ++b){ float *squared = layer.squared_gpu + w*h*c*b; float *norms = layer.norms_gpu + w*h*c*b; - float *input = state.input + w*h*c*b; - pow_ongpu(w*h*c, 2, input, 1, squared, 1); + float *input = net.input_gpu + w*h*c*b; + pow_gpu(w*h*c, 2, input, 1, squared, 1); - const_ongpu(w*h, layer.kappa, norms, 1); + const_gpu(w*h, layer.kappa, norms, 1); for(k = 0; k < layer.size/2; ++k){ - axpy_ongpu(w*h, layer.alpha, squared + w*h*k, 1, norms, 1); + axpy_gpu(w*h, layer.alpha, squared + w*h*k, 1, norms, 1); } for(k = 1; k < layer.c; ++k){ - copy_ongpu(w*h, norms + w*h*(k-1), 1, norms + w*h*k, 1); + copy_gpu(w*h, norms + w*h*(k-1), 1, norms + w*h*k, 1); int prev = k - ((layer.size-1)/2) - 1; int next = k + (layer.size/2); - if(prev >= 0) axpy_ongpu(w*h, -layer.alpha, squared + w*h*prev, 1, norms + w*h*k, 1); - if(next < layer.c) axpy_ongpu(w*h, layer.alpha, squared + w*h*next, 1, norms + w*h*k, 1); + if(prev >= 0) axpy_gpu(w*h, -layer.alpha, squared + w*h*prev, 1, norms + w*h*k, 1); + if(next < layer.c) axpy_gpu(w*h, layer.alpha, squared + w*h*next, 1, norms + w*h*k, 1); } } - pow_ongpu(w*h*c*layer.batch, -layer.beta, layer.norms_gpu, 1, layer.output_gpu, 1); - mul_ongpu(w*h*c*layer.batch, state.input, 1, layer.output_gpu, 1); + pow_gpu(w*h*c*layer.batch, -layer.beta, layer.norms_gpu, 1, layer.output_gpu, 1); + mul_gpu(w*h*c*layer.batch, net.input_gpu, 1, layer.output_gpu, 1); } -void backward_normalization_layer_gpu(const layer layer, network_state state) +void backward_normalization_layer_gpu(const layer layer, network net) { // TODO This is approximate ;-) int w = layer.w; int h = layer.h; int c = layer.c; - pow_ongpu(w*h*c*layer.batch, -layer.beta, layer.norms_gpu, 1, state.delta, 1); - mul_ongpu(w*h*c*layer.batch, layer.delta_gpu, 1, state.delta, 1); + pow_gpu(w*h*c*layer.batch, -layer.beta, layer.norms_gpu, 1, net.delta_gpu, 1); + mul_gpu(w*h*c*layer.batch, layer.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/src/normalization_layer.h b/image.darknet/src/normalization_layer.h index ab32776..665baa5 100644 --- a/image.darknet/src/normalization_layer.h +++ b/image.darknet/src/normalization_layer.h @@ -7,13 +7,13 @@ layer make_normalization_layer(int batch, int w, int h, int c, int size, float alpha, float beta, float kappa); void resize_normalization_layer(layer *layer, int h, int w); -void forward_normalization_layer(const layer layer, network_state state); -void backward_normalization_layer(const layer layer, network_state state); +void forward_normalization_layer(const layer layer, network net); +void backward_normalization_layer(const layer layer, network net); void visualize_normalization_layer(layer layer, char *window); #ifdef GPU -void forward_normalization_layer_gpu(const layer layer, network_state state); -void backward_normalization_layer_gpu(const layer layer, network_state state); +void forward_normalization_layer_gpu(const layer layer, network net); +void backward_normalization_layer_gpu(const layer layer, network net); #endif #endif diff --git a/image.darknet/src/option_list.c b/image.darknet/src/option_list.c index f935af3..2f52781 100644 --- a/image.darknet/src/option_list.c +++ b/image.darknet/src/option_list.c @@ -32,6 +32,23 @@ list *read_data_cfg(char *filename) return options; } +metadata get_metadata(char *file) +{ + metadata m = {0}; + list *options = read_data_cfg(file); + + char *name_list = option_find_str(options, "names", 0); + if(!name_list) name_list = option_find_str(options, "labels", 0); + if(!name_list) { + fprintf(stderr, "No names or labels found\n"); + } else { + m.names = get_labels(name_list); + } + m.classes = option_find_int(options, "classes", 2); + free_list(options); + return m; +} + int read_option(char *s, list *options) { size_t i; diff --git a/image.darknet/src/option_list.h b/image.darknet/src/option_list.h index 054b3fd..844bd87 100644 --- a/image.darknet/src/option_list.h +++ b/image.darknet/src/option_list.h @@ -9,13 +9,9 @@ typedef struct{ } kvp; -list *read_data_cfg(char *filename); int read_option(char *s, list *options); void option_insert(list *l, char *key, char *val); char *option_find(list *l, char *key); -char *option_find_str(list *l, char *key, char *def); -int option_find_int(list *l, char *key, int def); -int option_find_int_quiet(list *l, char *key, int def); float option_find_float(list *l, char *key, float def); float option_find_float_quiet(list *l, char *key, float def); void option_unused(list *l); diff --git a/image.darknet/src/parser.c b/image.darknet/src/parser.c index 3f39a13..c8141c9 100644 --- a/image.darknet/src/parser.c +++ b/image.darknet/src/parser.c @@ -1,14 +1,17 @@ #include #include #include +#include #include "activation_layer.h" +#include "logistic_layer.h" +#include "l2norm_layer.h" #include "activations.h" -#include "assert.h" #include "avgpool_layer.h" #include "batchnorm_layer.h" #include "blas.h" #include "connected_layer.h" +#include "deconvolutional_layer.h" #include "convolutional_layer.h" #include "cost_layer.h" #include "crnn_layer.h" @@ -23,11 +26,15 @@ #include "option_list.h" #include "parser.h" #include "region_layer.h" +#include "yolo_layer.h" +#include "iseg_layer.h" #include "reorg_layer.h" #include "rnn_layer.h" #include "route_layer.h" +#include "upsample_layer.h" #include "shortcut_layer.h" #include "softmax_layer.h" +#include "lstm_layer.h" #include "utils.h" typedef struct{ @@ -45,14 +52,21 @@ LAYER_TYPE string_to_layer_type(char * type) if (strcmp(type, "[cost]")==0) return COST; if (strcmp(type, "[detection]")==0) return DETECTION; if (strcmp(type, "[region]")==0) return REGION; + if (strcmp(type, "[yolo]")==0) return YOLO; + if (strcmp(type, "[iseg]")==0) return ISEG; if (strcmp(type, "[local]")==0) return LOCAL; if (strcmp(type, "[conv]")==0 || strcmp(type, "[convolutional]")==0) return CONVOLUTIONAL; + if (strcmp(type, "[deconv]")==0 + || strcmp(type, "[deconvolutional]")==0) return DECONVOLUTIONAL; if (strcmp(type, "[activation]")==0) return ACTIVE; + if (strcmp(type, "[logistic]")==0) return LOGXENT; + if (strcmp(type, "[l2norm]")==0) return L2NORM; if (strcmp(type, "[net]")==0 || strcmp(type, "[network]")==0) return NETWORK; if (strcmp(type, "[crnn]")==0) return CRNN; if (strcmp(type, "[gru]")==0) return GRU; + if (strcmp(type, "[lstm]") == 0) return LSTM; if (strcmp(type, "[rnn]")==0) return RNN; if (strcmp(type, "[conn]")==0 || strcmp(type, "[connected]")==0) return CONNECTED; @@ -68,6 +82,7 @@ LAYER_TYPE string_to_layer_type(char * type) if (strcmp(type, "[soft]")==0 || strcmp(type, "[softmax]")==0) return SOFTMAX; if (strcmp(type, "[route]")==0) return ROUTE; + if (strcmp(type, "[upsample]")==0) return UPSAMPLE; return BLANK; } @@ -111,7 +126,7 @@ typedef struct size_params{ int c; int index; int time_steps; - network net; + network *net; } size_params; local_layer parse_local(list *options, size_params params) @@ -135,6 +150,32 @@ local_layer parse_local(list *options, size_params params) return layer; } +layer parse_deconvolutional(list *options, size_params params) +{ + int n = option_find_int(options, "filters",1); + int size = option_find_int(options, "size",1); + int stride = option_find_int(options, "stride",1); + + char *activation_s = option_find_str(options, "activation", "logistic"); + ACTIVATION activation = get_activation(activation_s); + + int batch,h,w,c; + h = params.h; + w = params.w; + c = params.c; + batch=params.batch; + if(!(h && w && c)) error("Layer before deconvolutional layer must output image."); + int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); + int pad = option_find_int_quiet(options, "pad",0); + int padding = option_find_int_quiet(options, "padding",0); + if(pad) padding = size/2; + + layer l = make_deconvolutional_layer(batch,h,w,c,n,size,stride,padding, activation, batch_normalize, params.net->adam); + + return l; +} + + convolutional_layer parse_convolutional(list *options, size_params params) { int n = option_find_int(options, "filters",1); @@ -142,6 +183,7 @@ convolutional_layer parse_convolutional(list *options, size_params params) int stride = option_find_int(options, "stride",1); int pad = option_find_int_quiet(options, "pad",0); int padding = option_find_int_quiet(options, "padding",0); + int groups = option_find_int_quiet(options, "groups", 1); if(pad) padding = size/2; char *activation_s = option_find_str(options, "activation", "logistic"); @@ -157,14 +199,9 @@ convolutional_layer parse_convolutional(list *options, size_params params) int binary = option_find_int_quiet(options, "binary", 0); int xnor = option_find_int_quiet(options, "xnor", 0); - convolutional_layer layer = make_convolutional_layer(batch,h,w,c,n,size,stride,padding,activation, batch_normalize, binary, xnor, params.net.adam); + convolutional_layer layer = make_convolutional_layer(batch,h,w,c,n,groups,size,stride,padding,activation, batch_normalize, binary, xnor, params.net->adam); layer.flipped = option_find_int_quiet(options, "flipped", 0); layer.dot = option_find_float_quiet(options, "dot", 0); - if(params.net.adam){ - layer.B1 = params.net.B1; - layer.B2 = params.net.B2; - layer.eps = params.net.eps; - } return layer; } @@ -187,13 +224,11 @@ layer parse_crnn(list *options, size_params params) layer parse_rnn(list *options, size_params params) { int output = option_find_int(options, "output",1); - int hidden = option_find_int(options, "hidden",1); char *activation_s = option_find_str(options, "activation", "logistic"); ACTIVATION activation = get_activation(activation_s); int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); - int logistic = option_find_int_quiet(options, "logistic", 0); - layer l = make_rnn_layer(params.batch, params.inputs, hidden, output, params.time_steps, activation, batch_normalize, logistic); + layer l = make_rnn_layer(params.batch, params.inputs, output, params.time_steps, activation, batch_normalize, params.net->adam); l.shortcut = option_find_int_quiet(options, "shortcut", 0); @@ -205,31 +240,114 @@ layer parse_gru(list *options, size_params params) int output = option_find_int(options, "output",1); int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); - layer l = make_gru_layer(params.batch, params.inputs, output, params.time_steps, batch_normalize); + layer l = make_gru_layer(params.batch, params.inputs, output, params.time_steps, batch_normalize, params.net->adam); + l.tanh = option_find_int_quiet(options, "tanh", 0); + + return l; +} + +layer parse_lstm(list *options, size_params params) +{ + int output = option_find_int(options, "output", 1); + int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); + + layer l = make_lstm_layer(params.batch, params.inputs, output, params.time_steps, batch_normalize, params.net->adam); return l; } -connected_layer parse_connected(list *options, size_params params) +layer parse_connected(list *options, size_params params) { int output = option_find_int(options, "output",1); char *activation_s = option_find_str(options, "activation", "logistic"); ACTIVATION activation = get_activation(activation_s); int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); - connected_layer layer = make_connected_layer(params.batch, params.inputs, output, activation, batch_normalize); - - return layer; + layer l = make_connected_layer(params.batch, params.inputs, output, activation, batch_normalize, params.net->adam); + return l; } -softmax_layer parse_softmax(list *options, size_params params) +layer parse_softmax(list *options, size_params params) { int groups = option_find_int_quiet(options, "groups",1); - softmax_layer layer = make_softmax_layer(params.batch, params.inputs, groups); - layer.temperature = option_find_float_quiet(options, "temperature", 1); + layer l = make_softmax_layer(params.batch, params.inputs, groups); + l.temperature = option_find_float_quiet(options, "temperature", 1); char *tree_file = option_find_str(options, "tree", 0); - if (tree_file) layer.softmax_tree = read_tree(tree_file); - return layer; + if (tree_file) l.softmax_tree = read_tree(tree_file); + l.w = params.w; + l.h = params.h; + l.c = params.c; + l.spatial = option_find_float_quiet(options, "spatial", 0); + l.noloss = option_find_int_quiet(options, "noloss", 0); + return l; +} + +int *parse_yolo_mask(char *a, int *num) +{ + int *mask = 0; + if(a){ + int len = strlen(a); + int n = 1; + int i; + for(i = 0; i < len; ++i){ + if (a[i] == ',') ++n; + } + mask = calloc(n, sizeof(int)); + for(i = 0; i < n; ++i){ + int val = atoi(a); + mask[i] = val; + a = strchr(a, ',')+1; + } + *num = n; + } + return mask; +} + +layer parse_yolo(list *options, size_params params) +{ + int classes = option_find_int(options, "classes", 20); + int total = option_find_int(options, "num", 1); + int num = total; + + char *a = option_find_str(options, "mask", 0); + int *mask = parse_yolo_mask(a, &num); + layer l = make_yolo_layer(params.batch, params.w, params.h, num, total, mask, classes); + assert(l.outputs == params.inputs); + + l.max_boxes = option_find_int_quiet(options, "max",90); + l.jitter = option_find_float(options, "jitter", .2); + + l.ignore_thresh = option_find_float(options, "ignore_thresh", .5); + l.truth_thresh = option_find_float(options, "truth_thresh", 1); + l.random = option_find_int_quiet(options, "random", 0); + + char *map_file = option_find_str(options, "map", 0); + if (map_file) l.map = read_map(map_file); + + a = option_find_str(options, "anchors", 0); + if(a){ + int len = strlen(a); + int n = 1; + int i; + for(i = 0; i < len; ++i){ + if (a[i] == ',') ++n; + } + for(i = 0; i < n; ++i){ + float bias = atof(a); + l.biases[i] = bias; + a = strchr(a, ',')+1; + } + } + return l; +} + +layer parse_iseg(list *options, size_params params) +{ + int classes = option_find_int(options, "classes", 20); + int ids = option_find_int(options, "ids", 32); + layer l = make_iseg_layer(params.batch, params.w, params.h, classes, ids); + assert(l.outputs == params.inputs); + return l; } layer parse_region(list *options, size_params params) @@ -245,6 +363,7 @@ layer parse_region(list *options, size_params params) l.sqrt = option_find_int_quiet(options, "sqrt", 0); l.softmax = option_find_int(options, "softmax", 0); + l.background = option_find_int_quiet(options, "background", 0); l.max_boxes = option_find_int_quiet(options, "max",30); l.jitter = option_find_float(options, "jitter", .2); l.rescore = option_find_int_quiet(options, "rescore",0); @@ -257,6 +376,7 @@ layer parse_region(list *options, size_params params) l.coord_scale = option_find_float(options, "coord_scale", 1); l.object_scale = option_find_float(options, "object_scale", 1); l.noobject_scale = option_find_float(options, "noobject_scale", 1); + l.mask_scale = option_find_float(options, "mask_scale", 1); l.class_scale = option_find_float(options, "class_scale", 1); l.bias_match = option_find_int_quiet(options, "bias_match",0); @@ -281,6 +401,7 @@ layer parse_region(list *options, size_params params) } return l; } + detection_layer parse_detection(list *options, size_params params) { int coords = option_find_int(options, "coords", 1); @@ -293,7 +414,7 @@ detection_layer parse_detection(list *options, size_params params) layer.softmax = option_find_int(options, "softmax", 0); layer.sqrt = option_find_int(options, "sqrt", 0); - layer.max_boxes = option_find_int_quiet(options, "max",30); + layer.max_boxes = option_find_int_quiet(options, "max",90); layer.coord_scale = option_find_float(options, "coord_scale", 1); layer.forced = option_find_int(options, "forced", 0); layer.object_scale = option_find_float(options, "object_scale", 1); @@ -312,6 +433,8 @@ cost_layer parse_cost(list *options, size_params params) float scale = option_find_float_quiet(options, "scale",1); cost_layer layer = make_cost_layer(params.batch, params.inputs, type, scale); layer.ratio = option_find_float_quiet(options, "ratio",0); + layer.noobject_scale = option_find_float_quiet(options, "noobj", 1); + layer.thresh = option_find_float_quiet(options, "thresh",0); return layer; } @@ -343,6 +466,8 @@ layer parse_reorg(list *options, size_params params) { int stride = option_find_int(options, "stride",1); int reverse = option_find_int_quiet(options, "reverse",0); + int flatten = option_find_int_quiet(options, "flatten",0); + int extra = option_find_int_quiet(options, "extra",0); int batch,h,w,c; h = params.h; @@ -351,7 +476,7 @@ layer parse_reorg(list *options, size_params params) batch=params.batch; if(!(h && w && c)) error("Layer before reorg layer must output image."); - layer layer = make_reorg_layer(batch,w,h,c,stride,reverse); + layer layer = make_reorg_layer(batch,w,h,c,stride,reverse, flatten, extra); return layer; } @@ -359,7 +484,7 @@ maxpool_layer parse_maxpool(list *options, size_params params) { int stride = option_find_int(options, "stride",1); int size = option_find_int(options, "size",stride); - int padding = option_find_int_quiet(options, "padding", (size-1)/2); + int padding = option_find_int_quiet(options, "padding", size-1); int batch,h,w,c; h = params.h; @@ -411,24 +536,45 @@ layer parse_batchnorm(list *options, size_params params) return l; } -layer parse_shortcut(list *options, size_params params, network net) +layer parse_shortcut(list *options, size_params params, network *net) { - char *l = option_find(options, "from"); + char *l = option_find(options, "from"); int index = atoi(l); if(index < 0) index = params.index + index; int batch = params.batch; - layer from = net.layers[index]; + layer from = net->layers[index]; layer s = make_shortcut_layer(batch, index, params.w, params.h, params.c, from.out_w, from.out_h, from.out_c); char *activation_s = option_find_str(options, "activation", "linear"); ACTIVATION activation = get_activation(activation_s); s.activation = activation; + s.alpha = option_find_float_quiet(options, "alpha", 1); + s.beta = option_find_float_quiet(options, "beta", 1); return s; } +layer parse_l2norm(list *options, size_params params) +{ + layer l = make_l2norm_layer(params.batch, params.inputs); + l.h = l.out_h = params.h; + l.w = l.out_w = params.w; + l.c = l.out_c = params.c; + return l; +} + + +layer parse_logistic(list *options, size_params params) +{ + layer l = make_logistic_layer(params.batch, params.inputs); + l.h = l.out_h = params.h; + l.w = l.out_w = params.w; + l.c = l.out_c = params.c; + return l; +} + layer parse_activation(list *options, size_params params) { char *activation_s = option_find_str(options, "activation", "linear"); @@ -436,19 +582,25 @@ layer parse_activation(list *options, size_params params) layer l = make_activation_layer(params.batch, params.inputs, activation); - l.out_h = params.h; - l.out_w = params.w; - l.out_c = params.c; - l.h = params.h; - l.w = params.w; - l.c = params.c; + l.h = l.out_h = params.h; + l.w = l.out_w = params.w; + l.c = l.out_c = params.c; + + return l; +} + +layer parse_upsample(list *options, size_params params, network *net) +{ + int stride = option_find_int(options, "stride",2); + layer l = make_upsample_layer(params.batch, params.w, params.h, params.c, stride); + l.scale = option_find_float_quiet(options, "scale", 1); return l; } -route_layer parse_route(list *options, size_params params, network net) +route_layer parse_route(list *options, size_params params, network *net) { - char *l = option_find(options, "layers"); + char *l = option_find(options, "layers"); int len = strlen(l); if(!l) error("Route Layer must specify input layers"); int n = 1; @@ -464,19 +616,19 @@ route_layer parse_route(list *options, size_params params, network net) l = strchr(l, ',')+1; if(index < 0) index = params.index + index; layers[i] = index; - sizes[i] = net.layers[index].outputs; + sizes[i] = net->layers[index].outputs; } int batch = params.batch; route_layer layer = make_route_layer(batch, n, layers, sizes); - convolutional_layer first = net.layers[layers[0]]; + convolutional_layer first = net->layers[layers[0]]; layer.out_w = first.out_w; layer.out_h = first.out_h; layer.out_c = first.out_c; for(i = 1; i < n; ++i){ int index = layers[i]; - convolutional_layer next = net.layers[index]; + convolutional_layer next = net->layers[index]; if(next.out_w == first.out_w && next.out_h == first.out_h){ layer.out_c += next.out_c; }else{ @@ -508,15 +660,17 @@ void parse_net_options(list *options, network *net) net->decay = option_find_float(options, "decay", .0001); int subdivs = option_find_int(options, "subdivisions",1); net->time_steps = option_find_int_quiet(options, "time_steps",1); + net->notruth = option_find_int_quiet(options, "notruth",0); net->batch /= subdivs; net->batch *= net->time_steps; net->subdivisions = subdivs; + net->random = option_find_int_quiet(options, "random", 0); net->adam = option_find_int_quiet(options, "adam", 0); if(net->adam){ net->B1 = option_find_float(options, "B1", .9); net->B2 = option_find_float(options, "B2", .999); - net->eps = option_find_float(options, "eps", .000001); + net->eps = option_find_float(options, "eps", .0000001); } net->h = option_find_int_quiet(options, "height",0); @@ -525,6 +679,10 @@ void parse_net_options(list *options, network *net) net->inputs = option_find_int_quiet(options, "inputs", net->h * net->w * net->c); net->max_crop = option_find_int_quiet(options, "max_crop",net->w*2); net->min_crop = option_find_int_quiet(options, "min_crop",net->w); + net->max_ratio = option_find_float_quiet(options, "max_ratio", (float) net->max_crop / net->w); + net->min_ratio = option_find_float_quiet(options, "min_ratio", (float) net->min_crop / net->w); + net->center = option_find_int_quiet(options, "center",0); + net->clip = option_find_float_quiet(options, "clip", 0); net->angle = option_find_float_quiet(options, "angle", 0); net->aspect = option_find_float_quiet(options, "aspect", 1); @@ -537,12 +695,13 @@ void parse_net_options(list *options, network *net) char *policy_s = option_find_str(options, "policy", "constant"); net->policy = get_policy(policy_s); net->burn_in = option_find_int_quiet(options, "burn_in", 0); + net->power = option_find_float_quiet(options, "power", 4); if(net->policy == STEP){ net->step = option_find_int(options, "step", 1); net->scale = option_find_float(options, "scale", 1); } else if (net->policy == STEPS){ - char *l = option_find(options, "steps"); - char *p = option_find(options, "scales"); + char *l = option_find(options, "steps"); + char *p = option_find(options, "scales"); if(!l || !p) error("STEPS policy must have steps and scales in cfg file"); int len = strlen(l); @@ -570,7 +729,6 @@ void parse_net_options(list *options, network *net) net->gamma = option_find_float(options, "gamma", 1); net->step = option_find_int(options, "step", 1); } else if (net->policy == POLY || net->policy == RANDOM){ - net->power = option_find_float(options, "power", 1); } net->max_batches = option_find_int(options, "max_batches", 0); } @@ -581,26 +739,26 @@ int is_network(section *s) || strcmp(s->type, "[network]")==0); } -network parse_network_cfg(char *filename) +network *parse_network_cfg(char *filename) { list *sections = read_cfg(filename); node *n = sections->front; if(!n) error("Config file has no sections"); - network net = make_network(sections->size - 1); - net.gpu_index = gpu_index; + network *net = make_network(sections->size - 1); + net->gpu_index = gpu_index; size_params params; section *s = (section *)n->val; list *options = s->options; if(!is_network(s)) error("First section must be [net] or [network]"); - parse_net_options(options, &net); - - params.h = net.h; - params.w = net.w; - params.c = net.c; - params.inputs = net.inputs; - params.batch = net.batch; - params.time_steps = net.time_steps; + parse_net_options(options, net); + + params.h = net->h; + params.w = net->w; + params.c = net->c; + params.inputs = net->inputs; + params.batch = net->batch; + params.time_steps = net->time_steps; params.net = net; size_t workspace_size = 0; @@ -617,14 +775,22 @@ network parse_network_cfg(char *filename) LAYER_TYPE lt = string_to_layer_type(s->type); if(lt == CONVOLUTIONAL){ l = parse_convolutional(options, params); + }else if(lt == DECONVOLUTIONAL){ + l = parse_deconvolutional(options, params); }else if(lt == LOCAL){ l = parse_local(options, params); }else if(lt == ACTIVE){ l = parse_activation(options, params); + }else if(lt == LOGXENT){ + l = parse_logistic(options, params); + }else if(lt == L2NORM){ + l = parse_l2norm(options, params); }else if(lt == RNN){ l = parse_rnn(options, params); }else if(lt == GRU){ l = parse_gru(options, params); + }else if (lt == LSTM) { + l = parse_lstm(options, params); }else if(lt == CRNN){ l = parse_crnn(options, params); }else if(lt == CONNECTED){ @@ -635,11 +801,15 @@ network parse_network_cfg(char *filename) l = parse_cost(options, params); }else if(lt == REGION){ l = parse_region(options, params); + }else if(lt == YOLO){ + l = parse_yolo(options, params); + }else if(lt == ISEG){ + l = parse_iseg(options, params); }else if(lt == DETECTION){ l = parse_detection(options, params); }else if(lt == SOFTMAX){ l = parse_softmax(options, params); - net.hierarchy = l.softmax_tree; + net->hierarchy = l.softmax_tree; }else if(lt == NORMALIZATION){ l = parse_normalization(options, params); }else if(lt == BATCHNORM){ @@ -652,23 +822,33 @@ network parse_network_cfg(char *filename) l = parse_avgpool(options, params); }else if(lt == ROUTE){ l = parse_route(options, params, net); + }else if(lt == UPSAMPLE){ + l = parse_upsample(options, params, net); }else if(lt == SHORTCUT){ l = parse_shortcut(options, params, net); }else if(lt == DROPOUT){ l = parse_dropout(options, params); - l.output = net.layers[count-1].output; - l.delta = net.layers[count-1].delta; + l.output = net->layers[count-1].output; + l.delta = net->layers[count-1].delta; #ifdef GPU - l.output_gpu = net.layers[count-1].output_gpu; - l.delta_gpu = net.layers[count-1].delta_gpu; + l.output_gpu = net->layers[count-1].output_gpu; + l.delta_gpu = net->layers[count-1].delta_gpu; #endif }else{ fprintf(stderr, "Type not recognized: %s\n", s->type); } + l.clip = net->clip; + l.truth = option_find_int_quiet(options, "truth", 0); + l.onlyforward = option_find_int_quiet(options, "onlyforward", 0); + l.stopbackward = option_find_int_quiet(options, "stopbackward", 0); + l.dontsave = option_find_int_quiet(options, "dontsave", 0); l.dontload = option_find_int_quiet(options, "dontload", 0); + l.numload = option_find_int_quiet(options, "numload", 0); l.dontloadscales = option_find_int_quiet(options, "dontloadscales", 0); + l.learning_rate_scale = option_find_float_quiet(options, "learning_rate", 1); + l.smooth = option_find_float_quiet(options, "smooth", 0); option_unused(options); - net.layers[count] = l; + net->layers[count] = l; if (l.workspace_size > workspace_size) workspace_size = l.workspace_size; free_section(s); n = n->next; @@ -679,20 +859,30 @@ network parse_network_cfg(char *filename) params.c = l.out_c; params.inputs = l.outputs; } - } + } free_list(sections); - net.outputs = get_network_output_size(net); - net.output = get_network_output(net); + layer out = get_network_output_layer(net); + net->outputs = out.outputs; + net->truths = out.outputs; + if(net->layers[net->n-1].truths) net->truths = net->layers[net->n-1].truths; + net->output = out.output; + net->input = calloc(net->inputs*net->batch, sizeof(float)); + net->truth = calloc(net->truths*net->batch, sizeof(float)); +#ifdef GPU + net->output_gpu = out.output_gpu; + net->input_gpu = cuda_make_array(net->input, net->inputs*net->batch); + net->truth_gpu = cuda_make_array(net->truth, net->truths*net->batch); +#endif if(workspace_size){ //printf("%ld\n", workspace_size); #ifdef GPU if(gpu_index >= 0){ - net.workspace = cuda_make_array(0, (workspace_size-1)/sizeof(float)+1); + net->workspace = cuda_make_array(0, (workspace_size-1)/sizeof(float)+1); }else { - net.workspace = calloc(1, workspace_size); + net->workspace = calloc(1, workspace_size); } #else - net.workspace = calloc(1, workspace_size); + net->workspace = calloc(1, workspace_size); #endif } return net; @@ -704,7 +894,7 @@ list *read_cfg(char *filename) if(file == 0) file_error(filename); char *line; int nu = 0; - list *sections = make_list(); + list *options = make_list(); section *current = 0; while((line=fgetl(file)) != 0){ ++ nu; @@ -712,7 +902,7 @@ list *read_cfg(char *filename) switch(line[0]){ case '[': current = malloc(sizeof(section)); - list_insert(sections, current); + list_insert(options, current); current->options = make_list(); current->type = line; break; @@ -730,7 +920,7 @@ list *read_cfg(char *filename) } } fclose(file); - return sections; + return options; } void save_convolutional_weights_binary(layer l, FILE *fp) @@ -776,7 +966,7 @@ void save_convolutional_weights(layer l, FILE *fp) pull_convolutional_layer(l); } #endif - int num = l.n*l.c*l.size*l.size; + int num = l.nweights; fwrite(l.biases, sizeof(float), l.n, fp); if (l.batch_normalize){ fwrite(l.scales, sizeof(float), l.n, fp); @@ -784,10 +974,6 @@ void save_convolutional_weights(layer l, FILE *fp) fwrite(l.rolling_variance, sizeof(float), l.n, fp); } fwrite(l.weights, sizeof(float), num, fp); - if(l.adam){ - fwrite(l.m, sizeof(float), num, fp); - fwrite(l.v, sizeof(float), num, fp); - } } void save_batchnorm_weights(layer l, FILE *fp) @@ -818,11 +1004,11 @@ void save_connected_weights(layer l, FILE *fp) } } -void save_weights_upto(network net, char *filename, int cutoff) +void save_weights_upto(network *net, char *filename, int cutoff) { #ifdef GPU - if(net.gpu_index >= 0){ - cuda_set_device(net.gpu_index); + if(net->gpu_index >= 0){ + cuda_set_device(net->gpu_index); } #endif fprintf(stderr, "Saving weights to %s\n", filename); @@ -830,17 +1016,18 @@ void save_weights_upto(network net, char *filename, int cutoff) if(!fp) file_error(filename); int major = 0; - int minor = 1; + int minor = 2; int revision = 0; fwrite(&major, sizeof(int), 1, fp); fwrite(&minor, sizeof(int), 1, fp); fwrite(&revision, sizeof(int), 1, fp); - fwrite(net.seen, sizeof(int), 1, fp); + fwrite(net->seen, sizeof(size_t), 1, fp); int i; - for(i = 0; i < net.n && i < cutoff; ++i){ - layer l = net.layers[i]; - if(l.type == CONVOLUTIONAL){ + for(i = 0; i < net->n && i < cutoff; ++i){ + layer l = net->layers[i]; + if (l.dontsave) continue; + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ save_convolutional_weights(l, fp); } if(l.type == CONNECTED){ save_connected_weights(l, fp); @@ -850,14 +1037,29 @@ void save_weights_upto(network net, char *filename, int cutoff) save_connected_weights(*(l.input_layer), fp); save_connected_weights(*(l.self_layer), fp); save_connected_weights(*(l.output_layer), fp); - } if(l.type == GRU){ - save_connected_weights(*(l.input_z_layer), fp); - save_connected_weights(*(l.input_r_layer), fp); - save_connected_weights(*(l.input_h_layer), fp); - save_connected_weights(*(l.state_z_layer), fp); - save_connected_weights(*(l.state_r_layer), fp); - save_connected_weights(*(l.state_h_layer), fp); - } if(l.type == CRNN){ + } if (l.type == LSTM) { + save_connected_weights(*(l.wi), fp); + save_connected_weights(*(l.wf), fp); + save_connected_weights(*(l.wo), fp); + save_connected_weights(*(l.wg), fp); + save_connected_weights(*(l.ui), fp); + save_connected_weights(*(l.uf), fp); + save_connected_weights(*(l.uo), fp); + save_connected_weights(*(l.ug), fp); + } if (l.type == GRU) { + if(1){ + save_connected_weights(*(l.wz), fp); + save_connected_weights(*(l.wr), fp); + save_connected_weights(*(l.wh), fp); + save_connected_weights(*(l.uz), fp); + save_connected_weights(*(l.ur), fp); + save_connected_weights(*(l.uh), fp); + }else{ + save_connected_weights(*(l.reset_layer), fp); + save_connected_weights(*(l.update_layer), fp); + save_connected_weights(*(l.state_layer), fp); + } + } if(l.type == CRNN){ save_convolutional_weights(*(l.input_layer), fp); save_convolutional_weights(*(l.self_layer), fp); save_convolutional_weights(*(l.output_layer), fp); @@ -875,9 +1077,9 @@ void save_weights_upto(network net, char *filename, int cutoff) } fclose(fp); } -void save_weights(network net, char *filename) +void save_weights(network *net, char *filename) { - save_weights_upto(net, filename, net.n); + save_weights_upto(net, filename, net->n); } void transpose_matrix(float *a, int rows, int cols) @@ -965,7 +1167,8 @@ void load_convolutional_weights(layer l, FILE *fp) //load_convolutional_weights_binary(l, fp); //return; } - int num = l.n*l.c*l.size*l.size; + if(l.numload) l.n = l.numload; + int num = l.c/l.groups*l.n*l.size*l.size; fread(l.biases, sizeof(float), l.n, fp); if (l.batch_normalize && (!l.dontloadscales)){ fread(l.scales, sizeof(float), l.n, fp); @@ -986,12 +1189,19 @@ void load_convolutional_weights(layer l, FILE *fp) fill_cpu(l.n, 0, l.rolling_mean, 1); fill_cpu(l.n, 0, l.rolling_variance, 1); } + if(0){ + int i; + for(i = 0; i < l.n; ++i){ + printf("%g, ", l.rolling_mean[i]); + } + printf("\n"); + for(i = 0; i < l.n; ++i){ + printf("%g, ", l.rolling_variance[i]); + } + printf("\n"); + } } fread(l.weights, sizeof(float), num, fp); - if(l.adam){ - fread(l.m, sizeof(float), num, fp); - fread(l.v, sizeof(float), num, fp); - } //if(l.c == 3) scal_cpu(num, 1./256, l.weights, 1); if (l.flipped) { transpose_matrix(l.weights, l.c*l.size*l.size, l.n); @@ -1005,7 +1215,7 @@ void load_convolutional_weights(layer l, FILE *fp) } -void load_weights_upto(network *net, char *filename, int cutoff) +void load_weights_upto(network *net, char *filename, int start, int cutoff) { #ifdef GPU if(net->gpu_index >= 0){ @@ -1023,14 +1233,20 @@ void load_weights_upto(network *net, char *filename, int cutoff) fread(&major, sizeof(int), 1, fp); fread(&minor, sizeof(int), 1, fp); fread(&revision, sizeof(int), 1, fp); - fread(net->seen, sizeof(int), 1, fp); + if ((major*10 + minor) >= 2 && major < 1000 && minor < 1000){ + fread(net->seen, sizeof(size_t), 1, fp); + } else { + int iseen = 0; + fread(&iseen, sizeof(int), 1, fp); + *net->seen = iseen; + } int transpose = (major > 1000) || (minor > 1000); int i; - for(i = 0; i < net->n && i < cutoff; ++i){ + for(i = start; i < net->n && i < cutoff; ++i){ layer l = net->layers[i]; if (l.dontload) continue; - if(l.type == CONVOLUTIONAL){ + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ load_convolutional_weights(l, fp); } if(l.type == CONNECTED){ @@ -1049,13 +1265,29 @@ void load_weights_upto(network *net, char *filename, int cutoff) load_connected_weights(*(l.self_layer), fp, transpose); load_connected_weights(*(l.output_layer), fp, transpose); } - if(l.type == GRU){ - load_connected_weights(*(l.input_z_layer), fp, transpose); - load_connected_weights(*(l.input_r_layer), fp, transpose); - load_connected_weights(*(l.input_h_layer), fp, transpose); - load_connected_weights(*(l.state_z_layer), fp, transpose); - load_connected_weights(*(l.state_r_layer), fp, transpose); - load_connected_weights(*(l.state_h_layer), fp, transpose); + if (l.type == LSTM) { + load_connected_weights(*(l.wi), fp, transpose); + load_connected_weights(*(l.wf), fp, transpose); + load_connected_weights(*(l.wo), fp, transpose); + load_connected_weights(*(l.wg), fp, transpose); + load_connected_weights(*(l.ui), fp, transpose); + load_connected_weights(*(l.uf), fp, transpose); + load_connected_weights(*(l.uo), fp, transpose); + load_connected_weights(*(l.ug), fp, transpose); + } + if (l.type == GRU) { + if(1){ + load_connected_weights(*(l.wz), fp, transpose); + load_connected_weights(*(l.wr), fp, transpose); + load_connected_weights(*(l.wh), fp, transpose); + load_connected_weights(*(l.uz), fp, transpose); + load_connected_weights(*(l.ur), fp, transpose); + load_connected_weights(*(l.uh), fp, transpose); + }else{ + load_connected_weights(*(l.reset_layer), fp, transpose); + load_connected_weights(*(l.update_layer), fp, transpose); + load_connected_weights(*(l.state_layer), fp, transpose); + } } if(l.type == LOCAL){ int locations = l.out_w*l.out_h; @@ -1075,6 +1307,6 @@ void load_weights_upto(network *net, char *filename, int cutoff) void load_weights(network *net, char *filename) { - load_weights_upto(net, filename, net->n); + load_weights_upto(net, filename, 0, net->n); } diff --git a/image.darknet/src/parser.h b/image.darknet/src/parser.h index 6cff4fb..81aef2c 100644 --- a/image.darknet/src/parser.h +++ b/image.darknet/src/parser.h @@ -1,13 +1,9 @@ #ifndef PARSER_H #define PARSER_H +#include "darknet.h" #include "network.h" -network parse_network_cfg(char *filename); void save_network(network net, char *filename); -void save_weights(network net, char *filename); -void save_weights_upto(network net, char *filename, int cutoff); void save_weights_double(network net, char *filename); -void load_weights(network *net, char *filename); -void load_weights_upto(network *net, char *filename, int cutoff); #endif diff --git a/image.darknet/src/region_layer.c b/image.darknet/src/region_layer.c index f5522c3..179f5e3 100644 --- a/image.darknet/src/region_layer.c +++ b/image.darknet/src/region_layer.c @@ -4,6 +4,7 @@ #include "box.h" #include "cuda.h" #include "utils.h" + #include #include #include @@ -18,6 +19,10 @@ layer make_region_layer(int batch, int w, int h, int n, int classes, int coords) l.batch = batch; l.h = h; l.w = w; + l.c = n*(classes + coords + 1); + l.out_w = l.w; + l.out_h = l.h; + l.out_c = l.c; l.classes = classes; l.coords = coords; l.cost = calloc(1, sizeof(float)); @@ -25,7 +30,7 @@ layer make_region_layer(int batch, int w, int h, int n, int classes, int coords) l.bias_updates = calloc(n*2, sizeof(float)); l.outputs = h*w*n*(classes + coords + 1); l.inputs = l.outputs; - l.truths = 30*(5); + l.truths = 30*(l.coords + 1); l.delta = calloc(batch*l.outputs, sizeof(float)); l.output = calloc(batch*l.outputs, sizeof(float)); int i; @@ -68,19 +73,19 @@ void resize_region_layer(layer *l, int w, int h) #endif } -box get_region_box(float *x, float *biases, int n, int index, int i, int j, int w, int h) +box get_region_box(float *x, float *biases, int n, int index, int i, int j, int w, int h, int stride) { box b; - b.x = (i + logistic_activate(x[index + 0])) / w; - b.y = (j + logistic_activate(x[index + 1])) / h; - b.w = exp(x[index + 2]) * biases[2*n] / w; - b.h = exp(x[index + 3]) * biases[2*n+1] / h; + b.x = (i + x[index + 0*stride]) / w; + b.y = (j + x[index + 1*stride]) / h; + b.w = exp(x[index + 2*stride]) * biases[2*n] / w; + b.h = exp(x[index + 3*stride]) * biases[2*n+1] / h; return b; } -float delta_region_box(box truth, float *x, float *biases, int n, int index, int i, int j, int w, int h, float *delta, float scale) +float delta_region_box(box truth, float *x, float *biases, int n, int index, int i, int j, int w, int h, float *delta, float scale, int stride) { - box pred = get_region_box(x, biases, n, index, i, j, w, h); + box pred = get_region_box(x, biases, n, index, i, j, w, h, stride); float iou = box_iou(pred, truth); float tx = (truth.x*w - i); @@ -88,34 +93,47 @@ float delta_region_box(box truth, float *x, float *biases, int n, int index, int float tw = log(truth.w*w / biases[2*n]); float th = log(truth.h*h / biases[2*n + 1]); - delta[index + 0] = scale * (tx - logistic_activate(x[index + 0])) * logistic_gradient(logistic_activate(x[index + 0])); - delta[index + 1] = scale * (ty - logistic_activate(x[index + 1])) * logistic_gradient(logistic_activate(x[index + 1])); - delta[index + 2] = scale * (tw - x[index + 2]); - delta[index + 3] = scale * (th - x[index + 3]); + delta[index + 0*stride] = scale * (tx - x[index + 0*stride]); + delta[index + 1*stride] = scale * (ty - x[index + 1*stride]); + delta[index + 2*stride] = scale * (tw - x[index + 2*stride]); + delta[index + 3*stride] = scale * (th - x[index + 3*stride]); return iou; } -void delta_region_class(float *output, float *delta, int index, int class, int classes, tree *hier, float scale, float *avg_cat) +void delta_region_mask(float *truth, float *x, int n, int index, float *delta, int stride, int scale) +{ + int i; + for(i = 0; i < n; ++i){ + delta[index + i*stride] = scale*(truth[i] - x[index + i*stride]); + } +} + + +void delta_region_class(float *output, float *delta, int index, int class, int classes, tree *hier, float scale, int stride, float *avg_cat, int tag) { int i, n; if(hier){ float pred = 1; while(class >= 0){ - pred *= output[index + class]; + pred *= output[index + stride*class]; int g = hier->group[class]; int offset = hier->group_offset[g]; for(i = 0; i < hier->group_size[g]; ++i){ - delta[index + offset + i] = scale * (0 - output[index + offset + i]); + delta[index + stride*(offset + i)] = scale * (0 - output[index + stride*(offset + i)]); } - delta[index + class] = scale * (1 - output[index + class]); + delta[index + stride*class] = scale * (1 - output[index + stride*class]); class = hier->parent[class]; } *avg_cat += pred; } else { + if (delta[index] && tag){ + delta[index + stride*class] = scale * (1 - output[index + stride*class]); + return; + } for(n = 0; n < classes; ++n){ - delta[index + n] = scale * (((n == class)?1 : 0) - output[index + n]); - if(n == class) *avg_cat += output[index + n]; + delta[index + stride*n] = scale * (((n == class)?1 : 0) - output[index + stride*n]); + if(n == class) *avg_cat += output[index + stride*n]; } } } @@ -130,42 +148,45 @@ float tisnan(float x) return (x != x); } -void softmax_tree(float *input, int batch, int inputs, float temp, tree *hierarchy, float *output); -void forward_region_layer(const layer l, network_state state) +int entry_index(layer l, int batch, int location, int entry) +{ + int n = location / (l.w*l.h); + int loc = location % (l.w*l.h); + return batch*l.outputs + n*l.w*l.h*(l.coords+l.classes+1) + entry*l.w*l.h + loc; +} + +void forward_region_layer(const layer l, network net) { int i,j,b,t,n; - int size = l.coords + l.classes + 1; - memcpy(l.output, state.input, l.outputs*l.batch*sizeof(float)); + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); + #ifndef GPU - flatten(l.output, l.w*l.h, size*l.n, l.batch, 1); -#endif for (b = 0; b < l.batch; ++b){ - for(i = 0; i < l.h*l.w*l.n; ++i){ - int index = size*i + b*l.outputs; - l.output[index + 4] = logistic_activate(l.output[index + 4]); + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + activate_array(l.output + index, 2*l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, l.coords); + if(!l.background) activate_array(l.output + index, l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, l.coords + 1); + if(!l.softmax && !l.softmax_tree) activate_array(l.output + index, l.classes*l.w*l.h, LOGISTIC); } } - - -#ifndef GPU if (l.softmax_tree){ - for (b = 0; b < l.batch; ++b){ - for(i = 0; i < l.h*l.w*l.n; ++i){ - int index = size*i + b*l.outputs; - softmax_tree(l.output + index + 5, 1, 0, 1, l.softmax_tree, l.output + index + 5); - } + int i; + int count = l.coords + 1; + for (i = 0; i < l.softmax_tree->groups; ++i) { + int group_size = l.softmax_tree->group_size[i]; + softmax_cpu(net.input + count, group_size, l.batch, l.inputs, l.n*l.w*l.h, 1, l.n*l.w*l.h, l.temperature, l.output + count); + count += group_size; } } else if (l.softmax){ - for (b = 0; b < l.batch; ++b){ - for(i = 0; i < l.h*l.w*l.n; ++i){ - int index = size*i + b*l.outputs; - softmax(l.output + index + 5, l.classes, 1, l.output + index + 5); - } - } + int index = entry_index(l, 0, 0, l.coords + !l.background); + softmax_cpu(net.input + index, l.classes + l.background, l.batch*l.n, l.inputs/l.n, l.w*l.h, 1, l.w*l.h, 1, l.output + index); } #endif - if(!state.train) return; + memset(l.delta, 0, l.outputs * l.batch * sizeof(float)); + if(!net.train) return; float avg_iou = 0; float recall = 0; float avg_cat = 0; @@ -178,26 +199,29 @@ void forward_region_layer(const layer l, network_state state) if(l.softmax_tree){ int onlyclass = 0; for(t = 0; t < 30; ++t){ - box truth = float_to_box(state.truth + t*5 + b*l.truths); + box truth = float_to_box(net.truth + t*(l.coords + 1) + b*l.truths, 1); if(!truth.x) break; - int class = state.truth[t*5 + b*l.truths + 4]; + int class = net.truth[t*(l.coords + 1) + b*l.truths + l.coords]; float maxp = 0; int maxi = 0; if(truth.x > 100000 && truth.y > 100000){ for(n = 0; n < l.n*l.w*l.h; ++n){ - int index = size*n + b*l.outputs + 5; - float scale = l.output[index-1]; - l.delta[index - 1] = l.noobject_scale * ((0 - l.output[index - 1]) * logistic_gradient(l.output[index - 1])); - float p = scale*get_hierarchy_probability(l.output + index, l.softmax_tree, class); + int class_index = entry_index(l, b, n, l.coords + 1); + int obj_index = entry_index(l, b, n, l.coords); + float scale = l.output[obj_index]; + l.delta[obj_index] = l.noobject_scale * (0 - l.output[obj_index]); + float p = scale*get_hierarchy_probability(l.output + class_index, l.softmax_tree, class, l.w*l.h); if(p > maxp){ maxp = p; maxi = n; } } - int index = size*maxi + b*l.outputs + 5; - delta_region_class(l.output, l.delta, index, class, l.classes, l.softmax_tree, l.class_scale, &avg_cat); - if(l.output[index - 1] < .3) l.delta[index - 1] = l.object_scale * ((.3 - l.output[index - 1]) * logistic_gradient(l.output[index - 1])); - else l.delta[index - 1] = 0; + int class_index = entry_index(l, b, maxi, l.coords + 1); + int obj_index = entry_index(l, b, maxi, l.coords); + delta_region_class(l.output, l.delta, class_index, class, l.classes, l.softmax_tree, l.class_scale, l.w*l.h, &avg_cat, !l.softmax); + if(l.output[obj_index] < .3) l.delta[obj_index] = l.object_scale * (.3 - l.output[obj_index]); + else l.delta[obj_index] = 0; + l.delta[obj_index] = 0; ++class_count; onlyclass = 1; break; @@ -208,190 +232,276 @@ void forward_region_layer(const layer l, network_state state) for (j = 0; j < l.h; ++j) { for (i = 0; i < l.w; ++i) { for (n = 0; n < l.n; ++n) { - int index = size*(j*l.w*l.n + i*l.n + n) + b*l.outputs; - box pred = get_region_box(l.output, l.biases, n, index, i, j, l.w, l.h); + int box_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 0); + box pred = get_region_box(l.output, l.biases, n, box_index, i, j, l.w, l.h, l.w*l.h); float best_iou = 0; for(t = 0; t < 30; ++t){ - box truth = float_to_box(state.truth + t*5 + b*l.truths); + box truth = float_to_box(net.truth + t*(l.coords + 1) + b*l.truths, 1); if(!truth.x) break; float iou = box_iou(pred, truth); if (iou > best_iou) { best_iou = iou; } } - avg_anyobj += l.output[index + 4]; - l.delta[index + 4] = l.noobject_scale * ((0 - l.output[index + 4]) * logistic_gradient(l.output[index + 4])); + int obj_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, l.coords); + avg_anyobj += l.output[obj_index]; + l.delta[obj_index] = l.noobject_scale * (0 - l.output[obj_index]); + if(l.background) l.delta[obj_index] = l.noobject_scale * (1 - l.output[obj_index]); if (best_iou > l.thresh) { - l.delta[index + 4] = 0; + l.delta[obj_index] = 0; } - if(*(state.net.seen) < 12800){ + if(*(net.seen) < 12800){ box truth = {0}; truth.x = (i + .5)/l.w; truth.y = (j + .5)/l.h; truth.w = l.biases[2*n]/l.w; truth.h = l.biases[2*n+1]/l.h; - delta_region_box(truth, l.output, l.biases, n, index, i, j, l.w, l.h, l.delta, .01); + delta_region_box(truth, l.output, l.biases, n, box_index, i, j, l.w, l.h, l.delta, .01, l.w*l.h); } } } } for(t = 0; t < 30; ++t){ - box truth = float_to_box(state.truth + t*5 + b*l.truths); + box truth = float_to_box(net.truth + t*(l.coords + 1) + b*l.truths, 1); if(!truth.x) break; float best_iou = 0; - int best_index = 0; int best_n = 0; i = (truth.x * l.w); j = (truth.y * l.h); - //printf("%d %f %d %f\n", i, truth.x*l.w, j, truth.y*l.h); box truth_shift = truth; truth_shift.x = 0; truth_shift.y = 0; - //printf("index %d %d\n",i, j); for(n = 0; n < l.n; ++n){ - int index = size*(j*l.w*l.n + i*l.n + n) + b*l.outputs; - box pred = get_region_box(l.output, l.biases, n, index, i, j, l.w, l.h); + int box_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 0); + box pred = get_region_box(l.output, l.biases, n, box_index, i, j, l.w, l.h, l.w*l.h); if(l.bias_match){ pred.w = l.biases[2*n]/l.w; pred.h = l.biases[2*n+1]/l.h; } - //printf("pred: (%f, %f) %f x %f\n", pred.x, pred.y, pred.w, pred.h); pred.x = 0; pred.y = 0; float iou = box_iou(pred, truth_shift); if (iou > best_iou){ - best_index = index; best_iou = iou; best_n = n; } } - //printf("%d %f (%f, %f) %f x %f\n", best_n, best_iou, truth.x, truth.y, truth.w, truth.h); - float iou = delta_region_box(truth, l.output, l.biases, best_n, best_index, i, j, l.w, l.h, l.delta, l.coord_scale); + int box_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, 0); + float iou = delta_region_box(truth, l.output, l.biases, best_n, box_index, i, j, l.w, l.h, l.delta, l.coord_scale * (2 - truth.w*truth.h), l.w*l.h); + if(l.coords > 4){ + int mask_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, 4); + delta_region_mask(net.truth + t*(l.coords + 1) + b*l.truths + 5, l.output, l.coords - 4, mask_index, l.delta, l.w*l.h, l.mask_scale); + } if(iou > .5) recall += 1; avg_iou += iou; - //l.delta[best_index + 4] = iou - l.output[best_index + 4]; - avg_obj += l.output[best_index + 4]; - l.delta[best_index + 4] = l.object_scale * (1 - l.output[best_index + 4]) * logistic_gradient(l.output[best_index + 4]); + int obj_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, l.coords); + avg_obj += l.output[obj_index]; + l.delta[obj_index] = l.object_scale * (1 - l.output[obj_index]); if (l.rescore) { - l.delta[best_index + 4] = l.object_scale * (iou - l.output[best_index + 4]) * logistic_gradient(l.output[best_index + 4]); + l.delta[obj_index] = l.object_scale * (iou - l.output[obj_index]); + } + if(l.background){ + l.delta[obj_index] = l.object_scale * (0 - l.output[obj_index]); } - - int class = state.truth[t*5 + b*l.truths + 4]; + int class = net.truth[t*(l.coords + 1) + b*l.truths + l.coords]; if (l.map) class = l.map[class]; - delta_region_class(l.output, l.delta, best_index + 5, class, l.classes, l.softmax_tree, l.class_scale, &avg_cat); + int class_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, l.coords + 1); + delta_region_class(l.output, l.delta, class_index, class, l.classes, l.softmax_tree, l.class_scale, l.w*l.h, &avg_cat, !l.softmax); ++count; ++class_count; } } - //printf("\n"); -#ifndef GPU - flatten(l.delta, l.w*l.h, size*l.n, l.batch, 0); -#endif *(l.cost) = pow(mag_array(l.delta, l.outputs * l.batch), 2); printf("Region Avg IOU: %f, Class: %f, Obj: %f, No Obj: %f, Avg Recall: %f, count: %d\n", avg_iou/count, avg_cat/class_count, avg_obj/count, avg_anyobj/(l.w*l.h*l.n*l.batch), recall/count, count); } -void backward_region_layer(const layer l, network_state state) +void backward_region_layer(const layer l, network net) { - axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, state.delta, 1); + /* + int b; + int size = l.coords + l.classes + 1; + for (b = 0; b < l.batch*l.n; ++b){ + int index = (b*size + 4)*l.w*l.h; + gradient_array(l.output + index, l.w*l.h, LOGISTIC, l.delta + index); + } + axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, net.delta, 1); + */ +} + +void correct_region_boxes(detection *dets, int n, int w, int h, int netw, int neth, int relative) +{ + int i; + int new_w=0; + int new_h=0; + if (((float)netw/w) < ((float)neth/h)) { + new_w = netw; + new_h = (h * netw)/w; + } else { + new_h = neth; + new_w = (w * neth)/h; + } + for (i = 0; i < n; ++i){ + box b = dets[i].bbox; + b.x = (b.x - (netw - new_w)/2./netw) / ((float)new_w/netw); + b.y = (b.y - (neth - new_h)/2./neth) / ((float)new_h/neth); + b.w *= (float)netw/new_w; + b.h *= (float)neth/new_h; + if(!relative){ + b.x *= w; + b.w *= w; + b.y *= h; + b.h *= h; + } + dets[i].bbox = b; + } } -void get_region_boxes(layer l, int w, int h, float thresh, float **probs, box *boxes, int only_objectness, int *map, float tree_thresh) +void get_region_detections(layer l, int w, int h, int netw, int neth, float thresh, int *map, float tree_thresh, int relative, detection *dets) { - int i,j,n; + int i,j,n,z; float *predictions = l.output; + if (l.batch == 2) { + float *flip = l.output + l.outputs; + for (j = 0; j < l.h; ++j) { + for (i = 0; i < l.w/2; ++i) { + for (n = 0; n < l.n; ++n) { + for(z = 0; z < l.classes + l.coords + 1; ++z){ + int i1 = z*l.w*l.h*l.n + n*l.w*l.h + j*l.w + i; + int i2 = z*l.w*l.h*l.n + n*l.w*l.h + j*l.w + (l.w - i - 1); + float swap = flip[i1]; + flip[i1] = flip[i2]; + flip[i2] = swap; + if(z == 0){ + flip[i1] = -flip[i1]; + flip[i2] = -flip[i2]; + } + } + } + } + } + for(i = 0; i < l.outputs; ++i){ + l.output[i] = (l.output[i] + flip[i])/2.; + } + } for (i = 0; i < l.w*l.h; ++i){ int row = i / l.w; int col = i % l.w; for(n = 0; n < l.n; ++n){ - int index = i*l.n + n; - int p_index = index * (l.classes + 5) + 4; - float scale = predictions[p_index]; - int box_index = index * (l.classes + 5); - boxes[index] = get_region_box(predictions, l.biases, n, box_index, col, row, l.w, l.h); - boxes[index].x *= w; - boxes[index].y *= h; - boxes[index].w *= w; - boxes[index].h *= h; - - int class_index = index * (l.classes + 5) + 5; + int index = n*l.w*l.h + i; + for(j = 0; j < l.classes; ++j){ + dets[index].prob[j] = 0; + } + int obj_index = entry_index(l, 0, n*l.w*l.h + i, l.coords); + int box_index = entry_index(l, 0, n*l.w*l.h + i, 0); + int mask_index = entry_index(l, 0, n*l.w*l.h + i, 4); + float scale = l.background ? 1 : predictions[obj_index]; + dets[index].bbox = get_region_box(predictions, l.biases, n, box_index, col, row, l.w, l.h, l.w*l.h); + dets[index].objectness = scale > thresh ? scale : 0; + if(dets[index].mask){ + for(j = 0; j < l.coords - 4; ++j){ + dets[index].mask[j] = l.output[mask_index + j*l.w*l.h]; + } + } + + int class_index = entry_index(l, 0, n*l.w*l.h + i, l.coords + !l.background); if(l.softmax_tree){ - hierarchy_predictions(predictions + class_index, l.classes, l.softmax_tree, 0); + hierarchy_predictions(predictions + class_index, l.classes, l.softmax_tree, 0, l.w*l.h); if(map){ for(j = 0; j < 200; ++j){ - float prob = scale*predictions[class_index+map[j]]; - probs[index][j] = (prob > thresh) ? prob : 0; + int class_index = entry_index(l, 0, n*l.w*l.h + i, l.coords + 1 + map[j]); + float prob = scale*predictions[class_index]; + dets[index].prob[j] = (prob > thresh) ? prob : 0; } } else { - int j = hierarchy_top_prediction(predictions + class_index, l.softmax_tree, tree_thresh); - probs[index][j] = (scale > thresh) ? scale : 0; - probs[index][l.classes] = scale; + int j = hierarchy_top_prediction(predictions + class_index, l.softmax_tree, tree_thresh, l.w*l.h); + dets[index].prob[j] = (scale > thresh) ? scale : 0; } } else { - for(j = 0; j < l.classes; ++j){ - float prob = scale*predictions[class_index+j]; - probs[index][j] = (prob > thresh) ? prob : 0; + if(dets[index].objectness){ + for(j = 0; j < l.classes; ++j){ + int class_index = entry_index(l, 0, n*l.w*l.h + i, l.coords + 1 + j); + float prob = scale*predictions[class_index]; + dets[index].prob[j] = (prob > thresh) ? prob : 0; + } } } - if(only_objectness){ - probs[index][0] = scale; - } } } + correct_region_boxes(dets, l.w*l.h*l.n, w, h, netw, neth, relative); } #ifdef GPU -void forward_region_layer_gpu(const layer l, network_state state) +void forward_region_layer_gpu(const layer l, network net) { - /* - if(!state.train){ - copy_ongpu(l.batch*l.inputs, state.input, 1, l.output_gpu, 1); - return; - } - */ - flatten_ongpu(state.input, l.h*l.w, l.n*(l.coords + l.classes + 1), l.batch, 1, l.output_gpu); - if(l.softmax_tree){ - int i; - int count = 5; - for (i = 0; i < l.softmax_tree->groups; ++i) { - int group_size = l.softmax_tree->group_size[i]; - softmax_gpu(l.output_gpu+count, group_size, l.classes + 5, l.w*l.h*l.n*l.batch, 1, l.output_gpu + count); - count += group_size; + copy_gpu(l.batch*l.inputs, net.input_gpu, 1, l.output_gpu, 1); + int b, n; + for (b = 0; b < l.batch; ++b){ + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + activate_array_gpu(l.output_gpu + index, 2*l.w*l.h, LOGISTIC); + if(l.coords > 4){ + index = entry_index(l, b, n*l.w*l.h, 4); + activate_array_gpu(l.output_gpu + index, (l.coords - 4)*l.w*l.h, LOGISTIC); + } + index = entry_index(l, b, n*l.w*l.h, l.coords); + if(!l.background) activate_array_gpu(l.output_gpu + index, l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, l.coords + 1); + if(!l.softmax && !l.softmax_tree) activate_array_gpu(l.output_gpu + index, l.classes*l.w*l.h, LOGISTIC); } - }else if (l.softmax){ - softmax_gpu(l.output_gpu+5, l.classes, l.classes + 5, l.w*l.h*l.n*l.batch, 1, l.output_gpu + 5); } - - float *in_cpu = calloc(l.batch*l.inputs, sizeof(float)); - float *truth_cpu = 0; - if(state.truth){ - int num_truth = l.batch*l.truths; - truth_cpu = calloc(num_truth, sizeof(float)); - cuda_pull_array(state.truth, truth_cpu, num_truth); + if (l.softmax_tree){ + int index = entry_index(l, 0, 0, l.coords + 1); + softmax_tree(net.input_gpu + index, l.w*l.h, l.batch*l.n, l.inputs/l.n, 1, l.output_gpu + index, *l.softmax_tree); + } else if (l.softmax) { + int index = entry_index(l, 0, 0, l.coords + !l.background); + softmax_gpu(net.input_gpu + index, l.classes + l.background, l.batch*l.n, l.inputs/l.n, l.w*l.h, 1, l.w*l.h, 1, l.output_gpu + index); } - cuda_pull_array(l.output_gpu, in_cpu, l.batch*l.inputs); - network_state cpu_state = state; - cpu_state.train = state.train; - cpu_state.truth = truth_cpu; - cpu_state.input = in_cpu; - forward_region_layer(l, cpu_state); + if(!net.train || l.onlyforward){ + cuda_pull_array(l.output_gpu, l.output, l.batch*l.outputs); + return; + } + + cuda_pull_array(l.output_gpu, net.input, l.batch*l.inputs); + forward_region_layer(l, net); //cuda_push_array(l.output_gpu, l.output, l.batch*l.outputs); - free(cpu_state.input); - if(!state.train) return; + if(!net.train) return; cuda_push_array(l.delta_gpu, l.delta, l.batch*l.outputs); - if(cpu_state.truth) free(cpu_state.truth); } -void backward_region_layer_gpu(layer l, network_state state) +void backward_region_layer_gpu(const layer l, network net) { - flatten_ongpu(l.delta_gpu, l.h*l.w, l.n*(l.coords + l.classes + 1), l.batch, 0, state.delta); + int b, n; + for (b = 0; b < l.batch; ++b){ + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + gradient_array_gpu(l.output_gpu + index, 2*l.w*l.h, LOGISTIC, l.delta_gpu + index); + if(l.coords > 4){ + index = entry_index(l, b, n*l.w*l.h, 4); + gradient_array_gpu(l.output_gpu + index, (l.coords - 4)*l.w*l.h, LOGISTIC, l.delta_gpu + index); + } + index = entry_index(l, b, n*l.w*l.h, l.coords); + if(!l.background) gradient_array_gpu(l.output_gpu + index, l.w*l.h, LOGISTIC, l.delta_gpu + index); + } + } + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); } #endif +void zero_objectness(layer l) +{ + int i, n; + for (i = 0; i < l.w*l.h; ++i){ + for(n = 0; n < l.n; ++n){ + int obj_index = entry_index(l, 0, n*l.w*l.h + i, l.coords); + l.output[obj_index] = 0; + } + } +} + diff --git a/image.darknet/src/region_layer.h b/image.darknet/src/region_layer.h index 9a3b7cd..9f12fd1 100644 --- a/image.darknet/src/region_layer.h +++ b/image.darknet/src/region_layer.h @@ -1,18 +1,18 @@ #ifndef REGION_LAYER_H #define REGION_LAYER_H +#include "darknet.h" #include "layer.h" #include "network.h" -layer make_region_layer(int batch, int h, int w, int n, int classes, int coords); -void forward_region_layer(const layer l, network_state state); -void backward_region_layer(const layer l, network_state state); -void get_region_boxes(layer l, int w, int h, float thresh, float **probs, box *boxes, int only_objectness, int *map, float tree_thresh); +layer make_region_layer(int batch, int w, int h, int n, int classes, int coords); +void forward_region_layer(const layer l, network net); +void backward_region_layer(const layer l, network net); void resize_region_layer(layer *l, int w, int h); #ifdef GPU -void forward_region_layer_gpu(const layer l, network_state state); -void backward_region_layer_gpu(layer l, network_state state); +void forward_region_layer_gpu(const layer l, network net); +void backward_region_layer_gpu(layer l, network net); #endif #endif diff --git a/image.darknet/src/reorg_layer.c b/image.darknet/src/reorg_layer.c index 2abca8f..31d6b84 100644 --- a/image.darknet/src/reorg_layer.c +++ b/image.darknet/src/reorg_layer.c @@ -1,18 +1,21 @@ #include "reorg_layer.h" #include "cuda.h" #include "blas.h" + #include -layer make_reorg_layer(int batch, int w, int h, int c, int stride, int reverse) +layer make_reorg_layer(int batch, int w, int h, int c, int stride, int reverse, int flatten, int extra) { layer l = {0}; l.type = REORG; l.batch = batch; l.stride = stride; + l.extra = extra; l.h = h; l.w = w; l.c = c; + l.flatten = flatten; if(reverse){ l.out_w = w*stride; l.out_h = h*stride; @@ -23,10 +26,20 @@ layer make_reorg_layer(int batch, int w, int h, int c, int stride, int reverse) l.out_c = c*(stride*stride); } l.reverse = reverse; - fprintf(stderr, "reorg /%2d %4d x%4d x%4d -> %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c); + l.outputs = l.out_h * l.out_w * l.out_c; l.inputs = h*w*c; - int output_size = l.out_h * l.out_w * l.out_c * batch; + if(l.extra){ + l.out_w = l.out_h = l.out_c = 0; + l.outputs = l.inputs + l.extra; + } + + if(extra){ + fprintf(stderr, "reorg %4d -> %4d\n", l.inputs, l.outputs); + } else { + fprintf(stderr, "reorg /%2d %4d x%4d x%4d -> %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c); + } + int output_size = l.outputs * batch; l.output = calloc(output_size, sizeof(float)); l.delta = calloc(output_size, sizeof(float)); @@ -75,40 +88,86 @@ void resize_reorg_layer(layer *l, int w, int h) #endif } -void forward_reorg_layer(const layer l, network_state state) +void forward_reorg_layer(const layer l, network net) { - if(l.reverse){ - reorg_cpu(state.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.output); - }else { - reorg_cpu(state.input, l.w, l.h, l.c, l.batch, l.stride, 0, l.output); + int i; + if(l.flatten){ + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); + if(l.reverse){ + flatten(l.output, l.w*l.h, l.c, l.batch, 0); + }else{ + flatten(l.output, l.w*l.h, l.c, l.batch, 1); + } + } else if (l.extra) { + for(i = 0; i < l.batch; ++i){ + copy_cpu(l.inputs, net.input + i*l.inputs, 1, l.output + i*l.outputs, 1); + } + } else if (l.reverse){ + reorg_cpu(net.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.output); + } else { + reorg_cpu(net.input, l.w, l.h, l.c, l.batch, l.stride, 0, l.output); } } -void backward_reorg_layer(const layer l, network_state state) +void backward_reorg_layer(const layer l, network net) { - if(l.reverse){ - reorg_cpu(l.delta, l.w, l.h, l.c, l.batch, l.stride, 0, state.delta); + int i; + if(l.flatten){ + memcpy(net.delta, l.delta, l.outputs*l.batch*sizeof(float)); + if(l.reverse){ + flatten(net.delta, l.w*l.h, l.c, l.batch, 1); + }else{ + flatten(net.delta, l.w*l.h, l.c, l.batch, 0); + } + } else if(l.reverse){ + reorg_cpu(l.delta, l.w, l.h, l.c, l.batch, l.stride, 0, net.delta); + } else if (l.extra) { + for(i = 0; i < l.batch; ++i){ + copy_cpu(l.inputs, l.delta + i*l.outputs, 1, net.delta + i*l.inputs, 1); + } }else{ - reorg_cpu(l.delta, l.w, l.h, l.c, l.batch, l.stride, 1, state.delta); + reorg_cpu(l.delta, l.w, l.h, l.c, l.batch, l.stride, 1, net.delta); } } #ifdef GPU -void forward_reorg_layer_gpu(layer l, network_state state) +void forward_reorg_layer_gpu(layer l, network net) { - if(l.reverse){ - reorg_ongpu(state.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.output_gpu); + int i; + if(l.flatten){ + if(l.reverse){ + flatten_gpu(net.input_gpu, l.w*l.h, l.c, l.batch, 0, l.output_gpu); + }else{ + flatten_gpu(net.input_gpu, l.w*l.h, l.c, l.batch, 1, l.output_gpu); + } + } else if (l.extra) { + for(i = 0; i < l.batch; ++i){ + copy_gpu(l.inputs, net.input_gpu + i*l.inputs, 1, l.output_gpu + i*l.outputs, 1); + } + } else if (l.reverse) { + reorg_gpu(net.input_gpu, l.w, l.h, l.c, l.batch, l.stride, 1, l.output_gpu); }else { - reorg_ongpu(state.input, l.w, l.h, l.c, l.batch, l.stride, 0, l.output_gpu); + reorg_gpu(net.input_gpu, l.w, l.h, l.c, l.batch, l.stride, 0, l.output_gpu); } } -void backward_reorg_layer_gpu(layer l, network_state state) +void backward_reorg_layer_gpu(layer l, network net) { - if(l.reverse){ - reorg_ongpu(l.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 0, state.delta); - }else{ - reorg_ongpu(l.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 1, state.delta); + if(l.flatten){ + if(l.reverse){ + flatten_gpu(l.delta_gpu, l.w*l.h, l.c, l.batch, 1, net.delta_gpu); + }else{ + flatten_gpu(l.delta_gpu, l.w*l.h, l.c, l.batch, 0, net.delta_gpu); + } + } else if (l.extra) { + int i; + for(i = 0; i < l.batch; ++i){ + copy_gpu(l.inputs, l.delta_gpu + i*l.outputs, 1, net.delta_gpu + i*l.inputs, 1); + } + } else if(l.reverse){ + reorg_gpu(l.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 0, net.delta_gpu); + } else { + reorg_gpu(l.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 1, net.delta_gpu); } } #endif diff --git a/image.darknet/src/reorg_layer.h b/image.darknet/src/reorg_layer.h index 21c22cd..e6513a5 100644 --- a/image.darknet/src/reorg_layer.h +++ b/image.darknet/src/reorg_layer.h @@ -6,14 +6,14 @@ #include "layer.h" #include "network.h" -layer make_reorg_layer(int batch, int h, int w, int c, int stride, int reverse); +layer make_reorg_layer(int batch, int w, int h, int c, int stride, int reverse, int flatten, int extra); void resize_reorg_layer(layer *l, int w, int h); -void forward_reorg_layer(const layer l, network_state state); -void backward_reorg_layer(const layer l, network_state state); +void forward_reorg_layer(const layer l, network net); +void backward_reorg_layer(const layer l, network net); #ifdef GPU -void forward_reorg_layer_gpu(layer l, network_state state); -void backward_reorg_layer_gpu(layer l, network_state state); +void forward_reorg_layer_gpu(layer l, network net); +void backward_reorg_layer_gpu(layer l, network net); #endif #endif diff --git a/image.darknet/src/rnn.c b/image.darknet/src/rnn.c deleted file mode 100644 index eca6f55..0000000 --- a/image.darknet/src/rnn.c +++ /dev/null @@ -1,492 +0,0 @@ -#include "network.h" -#include "cost_layer.h" -#include "utils.h" -#include "blas.h" -#include "parser.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -typedef struct { - float *x; - float *y; -} float_pair; - -int *read_tokenized_data(char *filename, size_t *read) -{ - size_t size = 512; - size_t count = 0; - FILE *fp = fopen(filename, "r"); - int *d = calloc(size, sizeof(int)); - int n, one; - one = fscanf(fp, "%d", &n); - while(one == 1){ - ++count; - if(count > size){ - size = size*2; - d = realloc(d, size*sizeof(int)); - } - d[count-1] = n; - one = fscanf(fp, "%d", &n); - } - fclose(fp); - d = realloc(d, count*sizeof(int)); - *read = count; - return d; -} - -char **read_tokens(char *filename, size_t *read) -{ - size_t size = 512; - size_t count = 0; - FILE *fp = fopen(filename, "r"); - char **d = calloc(size, sizeof(char *)); - char *line; - while((line=fgetl(fp)) != 0){ - ++count; - if(count > size){ - size = size*2; - d = realloc(d, size*sizeof(char *)); - } - d[count-1] = line; - } - fclose(fp); - d = realloc(d, count*sizeof(char *)); - *read = count; - return d; -} - -float_pair get_rnn_token_data(int *tokens, size_t *offsets, int characters, size_t len, int batch, int steps) -{ - float *x = calloc(batch * steps * characters, sizeof(float)); - float *y = calloc(batch * steps * characters, sizeof(float)); - int i,j; - for(i = 0; i < batch; ++i){ - for(j = 0; j < steps; ++j){ - int curr = tokens[(offsets[i])%len]; - int next = tokens[(offsets[i] + 1)%len]; - - x[(j*batch + i)*characters + curr] = 1; - y[(j*batch + i)*characters + next] = 1; - - offsets[i] = (offsets[i] + 1) % len; - - if(curr >= characters || curr < 0 || next >= characters || next < 0){ - error("Bad char"); - } - } - } - float_pair p; - p.x = x; - p.y = y; - return p; -} - -float_pair get_rnn_data(unsigned char *text, size_t *offsets, int characters, size_t len, int batch, int steps) -{ - float *x = calloc(batch * steps * characters, sizeof(float)); - float *y = calloc(batch * steps * characters, sizeof(float)); - int i,j; - for(i = 0; i < batch; ++i){ - for(j = 0; j < steps; ++j){ - unsigned char curr = text[(offsets[i])%len]; - unsigned char next = text[(offsets[i] + 1)%len]; - - x[(j*batch + i)*characters + curr] = 1; - y[(j*batch + i)*characters + next] = 1; - - offsets[i] = (offsets[i] + 1) % len; - - if(curr > 255 || curr <= 0 || next > 255 || next <= 0){ - /*text[(index+j+2)%len] = 0; - printf("%ld %d %d %d %d\n", index, j, len, (int)text[index+j], (int)text[index+j+1]); - printf("%s", text+index); - */ - error("Bad char"); - } - } - } - float_pair p; - p.x = x; - p.y = y; - return p; -} - -void reset_rnn_state(network net, int b) -{ - int i; - for (i = 0; i < net.n; ++i) { - #ifdef GPU - layer l = net.layers[i]; - if(l.state_gpu){ - fill_ongpu(l.outputs, 0, l.state_gpu + l.outputs*b, 1); - } - #endif - } -} - -void train_char_rnn(char *cfgfile, char *weightfile, char *filename, int clear, int tokenized) -{ - srand(time(0)); - unsigned char *text = 0; - int *tokens = 0; - size_t size; - if(tokenized){ - tokens = read_tokenized_data(filename, &size); - } else { - FILE *fp = fopen(filename, "rb"); - - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - text = calloc(size+1, sizeof(char)); - fread(text, 1, size, fp); - fclose(fp); - } - - char *backup_directory = "/home/pjreddie/backup/"; - char *base = basecfg(cfgfile); - fprintf(stderr, "%s\n", base); - float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - - int inputs = get_network_input_size(net); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int batch = net.batch; - int steps = net.time_steps; - if(clear) *net.seen = 0; - int i = (*net.seen)/net.batch; - - int streams = batch/steps; - size_t *offsets = calloc(streams, sizeof(size_t)); - int j; - for(j = 0; j < streams; ++j){ - offsets[j] = rand_size_t()%size; - } - - clock_t time; - while(get_current_batch(net) < net.max_batches){ - i += 1; - time=clock(); - float_pair p; - if(tokenized){ - p = get_rnn_token_data(tokens, offsets, inputs, size, streams, steps); - }else{ - p = get_rnn_data(text, offsets, inputs, size, streams, steps); - } - - float loss = train_network_datum(net, p.x, p.y) / (batch); - free(p.x); - free(p.y); - if (avg_loss < 0) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - - int chars = get_current_batch(net)*batch; - fprintf(stderr, "%d: %f, %f avg, %f rate, %lf seconds, %f epochs\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time), (float) chars/size); - - for(j = 0; j < streams; ++j){ - //printf("%d\n", j); - if(rand()%10 == 0){ - //fprintf(stderr, "Reset\n"); - offsets[j] = rand_size_t()%size; - reset_rnn_state(net, j); - } - } - - if(i%1000==0){ - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); - save_weights(net, buff); - } - if(i%10==0){ - char buff[256]; - sprintf(buff, "%s/%s.backup", backup_directory, base); - save_weights(net, buff); - } - } - char buff[256]; - sprintf(buff, "%s/%s_final.weights", backup_directory, base); - save_weights(net, buff); -} - -void print_symbol(int n, char **tokens){ - if(tokens){ - printf("%s ", tokens[n]); - } else { - printf("%c", n); - } -} - -void test_char_rnn(char *cfgfile, char *weightfile, int num, char *seed, float temp, int rseed, char *token_file) -{ - char **tokens = 0; - if(token_file){ - size_t n; - tokens = read_tokens(token_file, &n); - } - - srand(rseed); - char *base = basecfg(cfgfile); - fprintf(stderr, "%s\n", base); - - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int inputs = get_network_input_size(net); - - int i, j; - for(i = 0; i < net.n; ++i) net.layers[i].temperature = temp; - int c = 0; - int len = strlen(seed); - float *input = calloc(inputs, sizeof(float)); - - /* - fill_cpu(inputs, 0, input, 1); - for(i = 0; i < 10; ++i){ - network_predict(net, input); - } - fill_cpu(inputs, 0, input, 1); - */ - - for(i = 0; i < len-1; ++i){ - c = seed[i]; - input[c] = 1; - network_predict(net, input); - input[c] = 0; - print_symbol(c, tokens); - } - if(len) c = seed[len-1]; - print_symbol(c, tokens); - for(i = 0; i < num; ++i){ - input[c] = 1; - float *out = network_predict(net, input); - input[c] = 0; - for(j = 32; j < 127; ++j){ - //printf("%d %c %f\n",j, j, out[j]); - } - for(j = 0; j < inputs; ++j){ - if (out[j] < .0001) out[j] = 0; - } - c = sample_array(out, inputs); - print_symbol(c, tokens); - } - printf("\n"); -} - -void test_tactic_rnn(char *cfgfile, char *weightfile, int num, float temp, int rseed, char *token_file) -{ - char **tokens = 0; - if(token_file){ - size_t n; - tokens = read_tokens(token_file, &n); - } - - srand(rseed); - char *base = basecfg(cfgfile); - fprintf(stderr, "%s\n", base); - - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int inputs = get_network_input_size(net); - - int i, j; - for(i = 0; i < net.n; ++i) net.layers[i].temperature = temp; - int c = 0; - float *input = calloc(inputs, sizeof(float)); - float *out = 0; - - while((c = getc(stdin)) != EOF){ - input[c] = 1; - out = network_predict(net, input); - input[c] = 0; - } - for(i = 0; i < num; ++i){ - for(j = 0; j < inputs; ++j){ - if (out[j] < .0001) out[j] = 0; - } - int next = sample_array(out, inputs); - if(c == '.' && next == '\n') break; - c = next; - print_symbol(c, tokens); - - input[c] = 1; - out = network_predict(net, input); - input[c] = 0; - } - printf("\n"); -} - -void valid_tactic_rnn(char *cfgfile, char *weightfile, char *seed) -{ - char *base = basecfg(cfgfile); - fprintf(stderr, "%s\n", base); - - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int inputs = get_network_input_size(net); - - int count = 0; - int words = 1; - int c; - int len = strlen(seed); - float *input = calloc(inputs, sizeof(float)); - int i; - for(i = 0; i < len; ++i){ - c = seed[i]; - input[(int)c] = 1; - network_predict(net, input); - input[(int)c] = 0; - } - float sum = 0; - c = getc(stdin); - float log2 = log(2); - int in = 0; - while(c != EOF){ - int next = getc(stdin); - if(next == EOF) break; - if(next < 0 || next >= 255) error("Out of range character"); - - input[c] = 1; - float *out = network_predict(net, input); - input[c] = 0; - - if(c == '.' && next == '\n') in = 0; - if(!in) { - if(c == '>' && next == '>'){ - in = 1; - ++words; - } - c = next; - continue; - } - ++count; - sum += log(out[next])/log2; - c = next; - printf("%d %d Perplexity: %4.4f Word Perplexity: %4.4f\n", count, words, pow(2, -sum/count), pow(2, -sum/words)); - } -} - -void valid_char_rnn(char *cfgfile, char *weightfile, char *seed) -{ - char *base = basecfg(cfgfile); - fprintf(stderr, "%s\n", base); - - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int inputs = get_network_input_size(net); - - int count = 0; - int words = 1; - int c; - int len = strlen(seed); - float *input = calloc(inputs, sizeof(float)); - int i; - for(i = 0; i < len; ++i){ - c = seed[i]; - input[(int)c] = 1; - network_predict(net, input); - input[(int)c] = 0; - } - float sum = 0; - c = getc(stdin); - float log2 = log(2); - while(c != EOF){ - int next = getc(stdin); - if(next == EOF) break; - if(next < 0 || next >= 255) error("Out of range character"); - ++count; - if(next == ' ' || next == '\n' || next == '\t') ++words; - input[c] = 1; - float *out = network_predict(net, input); - input[c] = 0; - sum += log(out[next])/log2; - c = next; - printf("%d Perplexity: %4.4f Word Perplexity: %4.4f\n", count, pow(2, -sum/count), pow(2, -sum/words)); - } -} - -void vec_char_rnn(char *cfgfile, char *weightfile, char *seed) -{ - char *base = basecfg(cfgfile); - fprintf(stderr, "%s\n", base); - - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int inputs = get_network_input_size(net); - - int c; - int seed_len = strlen(seed); - float *input = calloc(inputs, sizeof(float)); - int i; - char *line; - while((line=fgetl(stdin)) != 0){ - reset_rnn_state(net, 0); - for(i = 0; i < seed_len; ++i){ - c = seed[i]; - input[(int)c] = 1; - network_predict(net, input); - input[(int)c] = 0; - } - strip(line); - int str_len = strlen(line); - for(i = 0; i < str_len; ++i){ - c = line[i]; - input[(int)c] = 1; - network_predict(net, input); - input[(int)c] = 0; - } - c = ' '; - input[(int)c] = 1; - network_predict(net, input); - input[(int)c] = 0; - - layer l = net.layers[0]; - #ifdef GPU - cuda_pull_array(l.output_gpu, l.output, l.outputs); - #endif - printf("%s", line); - for(i = 0; i < l.outputs; ++i){ - printf(",%g", l.output[i]); - } - printf("\n"); - } -} - -void run_char_rnn(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - char *filename = find_char_arg(argc, argv, "-file", "data/shakespeare.txt"); - char *seed = find_char_arg(argc, argv, "-seed", "\n\n"); - int len = find_int_arg(argc, argv, "-len", 1000); - float temp = find_float_arg(argc, argv, "-temp", .7); - int rseed = find_int_arg(argc, argv, "-srand", time(0)); - int clear = find_arg(argc, argv, "-clear"); - int tokenized = find_arg(argc, argv, "-tokenized"); - char *tokens = find_char_arg(argc, argv, "-tokens", 0); - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - if(0==strcmp(argv[2], "train")) train_char_rnn(cfg, weights, filename, clear, tokenized); - else if(0==strcmp(argv[2], "valid")) valid_char_rnn(cfg, weights, seed); - else if(0==strcmp(argv[2], "validtactic")) valid_tactic_rnn(cfg, weights, seed); - else if(0==strcmp(argv[2], "vec")) vec_char_rnn(cfg, weights, seed); - else if(0==strcmp(argv[2], "generate")) test_char_rnn(cfg, weights, len, seed, temp, rseed, tokens); - else if(0==strcmp(argv[2], "generatetactic")) test_tactic_rnn(cfg, weights, len, temp, rseed, tokens); -} diff --git a/image.darknet/src/rnn_layer.c b/image.darknet/src/rnn_layer.c index 83fda13..8c9b457 100644 --- a/image.darknet/src/rnn_layer.c +++ b/image.darknet/src/rnn_layer.c @@ -26,7 +26,7 @@ static void increment_layer(layer *l, int steps) #endif } -layer make_rnn_layer(int batch, int inputs, int hidden, int outputs, int steps, ACTIVATION activation, int batch_normalize, int log) +layer make_rnn_layer(int batch, int inputs, int outputs, int steps, ACTIVATION activation, int batch_normalize, int adam) { fprintf(stderr, "RNN Layer: %d inputs, %d outputs\n", inputs, outputs); batch = batch / steps; @@ -34,24 +34,24 @@ layer make_rnn_layer(int batch, int inputs, int hidden, int outputs, int steps, l.batch = batch; l.type = RNN; l.steps = steps; - l.hidden = hidden; l.inputs = inputs; - l.state = calloc(batch*hidden*(steps+1), sizeof(float)); + l.state = calloc(batch*outputs, sizeof(float)); + l.prev_state = calloc(batch*outputs, sizeof(float)); l.input_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_layer) = make_connected_layer(batch*steps, inputs, hidden, activation, batch_normalize); + *(l.input_layer) = make_connected_layer(batch*steps, inputs, outputs, activation, batch_normalize, adam); l.input_layer->batch = batch; l.self_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.self_layer) = make_connected_layer(batch*steps, hidden, hidden, (log==2)?LOGGY:(log==1?LOGISTIC:activation), batch_normalize); + *(l.self_layer) = make_connected_layer(batch*steps, outputs, outputs, activation, batch_normalize, adam); l.self_layer->batch = batch; l.output_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.output_layer) = make_connected_layer(batch*steps, hidden, outputs, activation, batch_normalize); + *(l.output_layer) = make_connected_layer(batch*steps, outputs, outputs, activation, batch_normalize, adam); l.output_layer->batch = batch; l.outputs = outputs; @@ -65,66 +65,72 @@ layer make_rnn_layer(int batch, int inputs, int hidden, int outputs, int steps, l.forward_gpu = forward_rnn_layer_gpu; l.backward_gpu = backward_rnn_layer_gpu; l.update_gpu = update_rnn_layer_gpu; - l.state_gpu = cuda_make_array(l.state, batch*hidden*(steps+1)); + l.state_gpu = cuda_make_array(0, batch*outputs); + l.prev_state_gpu = cuda_make_array(0, batch*outputs); l.output_gpu = l.output_layer->output_gpu; l.delta_gpu = l.output_layer->delta_gpu; +#ifdef CUDNN + cudnnSetTensor4dDescriptor(l.input_layer->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.input_layer->out_c, l.input_layer->out_h, l.input_layer->out_w); + cudnnSetTensor4dDescriptor(l.self_layer->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.self_layer->out_c, l.self_layer->out_h, l.self_layer->out_w); + cudnnSetTensor4dDescriptor(l.output_layer->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.output_layer->out_c, l.output_layer->out_h, l.output_layer->out_w); +#endif #endif return l; } -void update_rnn_layer(layer l, int batch, float learning_rate, float momentum, float decay) +void update_rnn_layer(layer l, update_args a) { - update_connected_layer(*(l.input_layer), batch, learning_rate, momentum, decay); - update_connected_layer(*(l.self_layer), batch, learning_rate, momentum, decay); - update_connected_layer(*(l.output_layer), batch, learning_rate, momentum, decay); + update_connected_layer(*(l.input_layer), a); + update_connected_layer(*(l.self_layer), a); + update_connected_layer(*(l.output_layer), a); } -void forward_rnn_layer(layer l, network_state state) +void forward_rnn_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); layer output_layer = *(l.output_layer); fill_cpu(l.outputs * l.batch * l.steps, 0, output_layer.delta, 1); - fill_cpu(l.hidden * l.batch * l.steps, 0, self_layer.delta, 1); - fill_cpu(l.hidden * l.batch * l.steps, 0, input_layer.delta, 1); - if(state.train) fill_cpu(l.hidden * l.batch, 0, l.state, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, self_layer.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, input_layer.delta, 1); + if(net.train) fill_cpu(l.outputs * l.batch, 0, l.state, 1); for (i = 0; i < l.steps; ++i) { - s.input = state.input; + s.input = net.input; forward_connected_layer(input_layer, s); s.input = l.state; forward_connected_layer(self_layer, s); float *old_state = l.state; - if(state.train) l.state += l.hidden*l.batch; + if(net.train) l.state += l.outputs*l.batch; if(l.shortcut){ - copy_cpu(l.hidden * l.batch, old_state, 1, l.state, 1); + copy_cpu(l.outputs * l.batch, old_state, 1, l.state, 1); }else{ - fill_cpu(l.hidden * l.batch, 0, l.state, 1); + fill_cpu(l.outputs * l.batch, 0, l.state, 1); } - axpy_cpu(l.hidden * l.batch, 1, input_layer.output, 1, l.state, 1); - axpy_cpu(l.hidden * l.batch, 1, self_layer.output, 1, l.state, 1); + axpy_cpu(l.outputs * l.batch, 1, input_layer.output, 1, l.state, 1); + axpy_cpu(l.outputs * l.batch, 1, self_layer.output, 1, l.state, 1); s.input = l.state; forward_connected_layer(output_layer, s); - state.input += l.inputs*l.batch; + net.input += l.inputs*l.batch; increment_layer(&input_layer, 1); increment_layer(&self_layer, 1); increment_layer(&output_layer, 1); } } -void backward_rnn_layer(layer l, network_state state) +void backward_rnn_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -134,34 +140,34 @@ void backward_rnn_layer(layer l, network_state state) increment_layer(&self_layer, l.steps-1); increment_layer(&output_layer, l.steps-1); - l.state += l.hidden*l.batch*l.steps; + l.state += l.outputs*l.batch*l.steps; for (i = l.steps-1; i >= 0; --i) { - copy_cpu(l.hidden * l.batch, input_layer.output, 1, l.state, 1); - axpy_cpu(l.hidden * l.batch, 1, self_layer.output, 1, l.state, 1); + copy_cpu(l.outputs * l.batch, input_layer.output, 1, l.state, 1); + axpy_cpu(l.outputs * l.batch, 1, self_layer.output, 1, l.state, 1); s.input = l.state; s.delta = self_layer.delta; backward_connected_layer(output_layer, s); - l.state -= l.hidden*l.batch; + l.state -= l.outputs*l.batch; /* if(i > 0){ - copy_cpu(l.hidden * l.batch, input_layer.output - l.hidden*l.batch, 1, l.state, 1); - axpy_cpu(l.hidden * l.batch, 1, self_layer.output - l.hidden*l.batch, 1, l.state, 1); + copy_cpu(l.outputs * l.batch, input_layer.output - l.outputs*l.batch, 1, l.state, 1); + axpy_cpu(l.outputs * l.batch, 1, self_layer.output - l.outputs*l.batch, 1, l.state, 1); }else{ - fill_cpu(l.hidden * l.batch, 0, l.state, 1); + fill_cpu(l.outputs * l.batch, 0, l.state, 1); } */ s.input = l.state; - s.delta = self_layer.delta - l.hidden*l.batch; + s.delta = self_layer.delta - l.outputs*l.batch; if (i == 0) s.delta = 0; backward_connected_layer(self_layer, s); - copy_cpu(l.hidden*l.batch, self_layer.delta, 1, input_layer.delta, 1); - if (i > 0 && l.shortcut) axpy_cpu(l.hidden*l.batch, 1, self_layer.delta, 1, self_layer.delta - l.hidden*l.batch, 1); - s.input = state.input + i*l.inputs*l.batch; - if(state.delta) s.delta = state.delta + i*l.inputs*l.batch; + copy_cpu(l.outputs*l.batch, self_layer.delta, 1, input_layer.delta, 1); + if (i > 0 && l.shortcut) axpy_cpu(l.outputs*l.batch, 1, self_layer.delta, 1, self_layer.delta - l.outputs*l.batch, 1); + s.input = net.input + i*l.inputs*l.batch; + if(net.delta) s.delta = net.delta + i*l.inputs*l.batch; else s.delta = 0; backward_connected_layer(input_layer, s); @@ -187,58 +193,56 @@ void push_rnn_layer(layer l) push_connected_layer(*(l.output_layer)); } -void update_rnn_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay) +void update_rnn_layer_gpu(layer l, update_args a) { - update_connected_layer_gpu(*(l.input_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.self_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.output_layer), batch, learning_rate, momentum, decay); + update_connected_layer_gpu(*(l.input_layer), a); + update_connected_layer_gpu(*(l.self_layer), a); + update_connected_layer_gpu(*(l.output_layer), a); } -void forward_rnn_layer_gpu(layer l, network_state state) +void forward_rnn_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = {0}; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); layer output_layer = *(l.output_layer); - fill_ongpu(l.outputs * l.batch * l.steps, 0, output_layer.delta_gpu, 1); - fill_ongpu(l.hidden * l.batch * l.steps, 0, self_layer.delta_gpu, 1); - fill_ongpu(l.hidden * l.batch * l.steps, 0, input_layer.delta_gpu, 1); - if(state.train) fill_ongpu(l.hidden * l.batch, 0, l.state_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, output_layer.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, self_layer.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, input_layer.delta_gpu, 1); + + if(net.train) { + fill_gpu(l.outputs * l.batch * l.steps, 0, l.delta_gpu, 1); + copy_gpu(l.outputs*l.batch, l.state_gpu, 1, l.prev_state_gpu, 1); + } for (i = 0; i < l.steps; ++i) { - s.input = state.input; + s.input_gpu = net.input_gpu; forward_connected_layer_gpu(input_layer, s); - s.input = l.state_gpu; + s.input_gpu = l.state_gpu; forward_connected_layer_gpu(self_layer, s); - float *old_state = l.state_gpu; - if(state.train) l.state_gpu += l.hidden*l.batch; - if(l.shortcut){ - copy_ongpu(l.hidden * l.batch, old_state, 1, l.state_gpu, 1); - }else{ - fill_ongpu(l.hidden * l.batch, 0, l.state_gpu, 1); - } - axpy_ongpu(l.hidden * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); - axpy_ongpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); + fill_gpu(l.outputs * l.batch, 0, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); - s.input = l.state_gpu; + s.input_gpu = l.state_gpu; forward_connected_layer_gpu(output_layer, s); - state.input += l.inputs*l.batch; + net.input_gpu += l.inputs*l.batch; increment_layer(&input_layer, 1); increment_layer(&self_layer, 1); increment_layer(&output_layer, 1); } } -void backward_rnn_layer_gpu(layer l, network_state state) +void backward_rnn_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = {0}; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -246,32 +250,43 @@ void backward_rnn_layer_gpu(layer l, network_state state) increment_layer(&input_layer, l.steps - 1); increment_layer(&self_layer, l.steps - 1); increment_layer(&output_layer, l.steps - 1); - l.state_gpu += l.hidden*l.batch*l.steps; + float *last_input = input_layer.output_gpu; + float *last_self = self_layer.output_gpu; for (i = l.steps-1; i >= 0; --i) { + fill_gpu(l.outputs * l.batch, 0, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); - s.input = l.state_gpu; - s.delta = self_layer.delta_gpu; + s.input_gpu = l.state_gpu; + s.delta_gpu = self_layer.delta_gpu; backward_connected_layer_gpu(output_layer, s); - l.state_gpu -= l.hidden*l.batch; + if(i != 0) { + fill_gpu(l.outputs * l.batch, 0, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, input_layer.output_gpu - l.outputs*l.batch, 1, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, self_layer.output_gpu - l.outputs*l.batch, 1, l.state_gpu, 1); + }else { + copy_gpu(l.outputs*l.batch, l.prev_state_gpu, 1, l.state_gpu, 1); + } - copy_ongpu(l.hidden*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); + copy_gpu(l.outputs*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); - s.input = l.state_gpu; - s.delta = self_layer.delta_gpu - l.hidden*l.batch; - if (i == 0) s.delta = 0; + s.input_gpu = l.state_gpu; + s.delta_gpu = (i > 0) ? self_layer.delta_gpu - l.outputs*l.batch : 0; + if (i == 0) s.delta_gpu = 0; backward_connected_layer_gpu(self_layer, s); - //copy_ongpu(l.hidden*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); - if (i > 0 && l.shortcut) axpy_ongpu(l.hidden*l.batch, 1, self_layer.delta_gpu, 1, self_layer.delta_gpu - l.hidden*l.batch, 1); - s.input = state.input + i*l.inputs*l.batch; - if(state.delta) s.delta = state.delta + i*l.inputs*l.batch; - else s.delta = 0; + s.input_gpu = net.input_gpu + i*l.inputs*l.batch; + if(net.delta_gpu) s.delta_gpu = net.delta_gpu + i*l.inputs*l.batch; + else s.delta_gpu = 0; backward_connected_layer_gpu(input_layer, s); increment_layer(&input_layer, -1); increment_layer(&self_layer, -1); increment_layer(&output_layer, -1); } + fill_gpu(l.outputs * l.batch, 0, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, last_input, 1, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, last_self, 1, l.state_gpu, 1); } #endif diff --git a/image.darknet/src/rnn_layer.h b/image.darknet/src/rnn_layer.h index bb9478b..270a63f 100644 --- a/image.darknet/src/rnn_layer.h +++ b/image.darknet/src/rnn_layer.h @@ -7,16 +7,16 @@ #include "network.h" #define USET -layer make_rnn_layer(int batch, int inputs, int hidden, int outputs, int steps, ACTIVATION activation, int batch_normalize, int log); +layer make_rnn_layer(int batch, int inputs, int outputs, int steps, ACTIVATION activation, int batch_normalize, int adam); -void forward_rnn_layer(layer l, network_state state); -void backward_rnn_layer(layer l, network_state state); -void update_rnn_layer(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_rnn_layer(layer l, network net); +void backward_rnn_layer(layer l, network net); +void update_rnn_layer(layer l, update_args a); #ifdef GPU -void forward_rnn_layer_gpu(layer l, network_state state); -void backward_rnn_layer_gpu(layer l, network_state state); -void update_rnn_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_rnn_layer_gpu(layer l, network net); +void backward_rnn_layer_gpu(layer l, network net); +void update_rnn_layer_gpu(layer l, update_args a); void push_rnn_layer(layer l); void pull_rnn_layer(layer l); #endif diff --git a/image.darknet/src/rnn_vid.c b/image.darknet/src/rnn_vid.c deleted file mode 100644 index 36912d6..0000000 --- a/image.darknet/src/rnn_vid.c +++ /dev/null @@ -1,211 +0,0 @@ -#include "network.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "blas.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -image get_image_from_stream(CvCapture *cap); -image ipl_to_image(IplImage* src); - -void reconstruct_picture(network net, float *features, image recon, image update, float rate, float momentum, float lambda, int smooth_size, int iters); - - -typedef struct { - float *x; - float *y; -} float_pair; - -float_pair get_rnn_vid_data(network net, char **files, int n, int batch, int steps) -{ - int b; - assert(net.batch == steps + 1); - image out_im = get_network_image(net); - int output_size = out_im.w*out_im.h*out_im.c; - printf("%d %d %d\n", out_im.w, out_im.h, out_im.c); - float *feats = calloc(net.batch*batch*output_size, sizeof(float)); - for(b = 0; b < batch; ++b){ - int input_size = net.w*net.h*net.c; - float *input = calloc(input_size*net.batch, sizeof(float)); - char *filename = files[rand()%n]; - CvCapture *cap = cvCaptureFromFile(filename); - int frames = cvGetCaptureProperty(cap, CV_CAP_PROP_FRAME_COUNT); - int index = rand() % (frames - steps - 2); - if (frames < (steps + 4)){ - --b; - free(input); - continue; - } - - printf("frames: %d, index: %d\n", frames, index); - cvSetCaptureProperty(cap, CV_CAP_PROP_POS_FRAMES, index); - - int i; - for(i = 0; i < net.batch; ++i){ - IplImage* src = cvQueryFrame(cap); - image im = ipl_to_image(src); - rgbgr_image(im); - image re = resize_image(im, net.w, net.h); - //show_image(re, "loaded"); - //cvWaitKey(10); - memcpy(input + i*input_size, re.data, input_size*sizeof(float)); - free_image(im); - free_image(re); - } - float *output = network_predict(net, input); - - free(input); - - for(i = 0; i < net.batch; ++i){ - memcpy(feats + (b + i*batch)*output_size, output + i*output_size, output_size*sizeof(float)); - } - - cvReleaseCapture(&cap); - } - - //printf("%d %d %d\n", out_im.w, out_im.h, out_im.c); - float_pair p = {0}; - p.x = feats; - p.y = feats + output_size*batch; //+ out_im.w*out_im.h*out_im.c; - - return p; -} - - -void train_vid_rnn(char *cfgfile, char *weightfile) -{ - char *train_videos = "data/vid/train.txt"; - char *backup_directory = "/home/pjreddie/backup/"; - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - int i = *net.seen/imgs; - - list *plist = get_paths(train_videos); - int N = plist->size; - char **paths = (char **)list_to_array(plist); - clock_t time; - int steps = net.time_steps; - int batch = net.batch / net.time_steps; - - network extractor = parse_network_cfg("cfg/extractor.cfg"); - load_weights(&extractor, "/home/pjreddie/trained/yolo-coco.conv"); - - while(get_current_batch(net) < net.max_batches){ - i += 1; - time=clock(); - float_pair p = get_rnn_vid_data(extractor, paths, N, batch, steps); - - float loss = train_network_datum(net, p.x, p.y) / (net.batch); - - - free(p.x); - if (avg_loss < 0) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - - fprintf(stderr, "%d: %f, %f avg, %f rate, %lf seconds\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time)); - if(i%100==0){ - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); - save_weights(net, buff); - } - if(i%10==0){ - char buff[256]; - sprintf(buff, "%s/%s.backup", backup_directory, base); - save_weights(net, buff); - } - } - char buff[256]; - sprintf(buff, "%s/%s_final.weights", backup_directory, base); - save_weights(net, buff); -} - - -image save_reconstruction(network net, image *init, float *feat, char *name, int i) -{ - image recon; - if (init) { - recon = copy_image(*init); - } else { - recon = make_random_image(net.w, net.h, 3); - } - - image update = make_image(net.w, net.h, 3); - reconstruct_picture(net, feat, recon, update, .01, .9, .1, 2, 50); - char buff[256]; - sprintf(buff, "%s%d", name, i); - save_image(recon, buff); - free_image(update); - return recon; -} - -void generate_vid_rnn(char *cfgfile, char *weightfile) -{ - network extractor = parse_network_cfg("cfg/extractor.recon.cfg"); - load_weights(&extractor, "/home/pjreddie/trained/yolo-coco.conv"); - - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&extractor, 1); - set_batch_network(&net, 1); - - int i; - CvCapture *cap = cvCaptureFromFile("/extra/vid/ILSVRC2015/Data/VID/snippets/val/ILSVRC2015_val_00007030.mp4"); - float *feat; - float *next; - image last; - for(i = 0; i < 25; ++i){ - image im = get_image_from_stream(cap); - image re = resize_image(im, extractor.w, extractor.h); - feat = network_predict(extractor, re.data); - if(i > 0){ - printf("%f %f\n", mean_array(feat, 14*14*512), variance_array(feat, 14*14*512)); - printf("%f %f\n", mean_array(next, 14*14*512), variance_array(next, 14*14*512)); - printf("%f\n", mse_array(feat, 14*14*512)); - axpy_cpu(14*14*512, -1, feat, 1, next, 1); - printf("%f\n", mse_array(next, 14*14*512)); - } - next = network_predict(net, feat); - - free_image(im); - - free_image(save_reconstruction(extractor, 0, feat, "feat", i)); - free_image(save_reconstruction(extractor, 0, next, "next", i)); - if (i==24) last = copy_image(re); - free_image(re); - } - for(i = 0; i < 30; ++i){ - next = network_predict(net, next); - image new = save_reconstruction(extractor, &last, next, "new", i); - free_image(last); - last = new; - } -} - -void run_vid_rnn(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - //char *filename = (argc > 5) ? argv[5]: 0; - if(0==strcmp(argv[2], "train")) train_vid_rnn(cfg, weights); - else if(0==strcmp(argv[2], "generate")) generate_vid_rnn(cfg, weights); -} -#else -void run_vid_rnn(int argc, char **argv){} -#endif - diff --git a/image.darknet/src/route_layer.c b/image.darknet/src/route_layer.c index dce7118..a8970a4 100644 --- a/image.darknet/src/route_layer.c +++ b/image.darknet/src/route_layer.c @@ -1,6 +1,7 @@ #include "route_layer.h" #include "cuda.h" #include "blas.h" + #include route_layer make_route_layer(int batch, int n, int *input_layers, int *input_sizes) @@ -70,13 +71,13 @@ void resize_route_layer(route_layer *l, network *net) } -void forward_route_layer(const route_layer l, network_state state) +void forward_route_layer(const route_layer l, network net) { int i, j; int offset = 0; for(i = 0; i < l.n; ++i){ int index = l.input_layers[i]; - float *input = state.net.layers[index].output; + float *input = net.layers[index].output; int input_size = l.input_sizes[i]; for(j = 0; j < l.batch; ++j){ copy_cpu(input_size, input + j*input_size, 1, l.output + offset + j*l.outputs, 1); @@ -85,13 +86,13 @@ void forward_route_layer(const route_layer l, network_state state) } } -void backward_route_layer(const route_layer l, network_state state) +void backward_route_layer(const route_layer l, network net) { int i, j; int offset = 0; for(i = 0; i < l.n; ++i){ int index = l.input_layers[i]; - float *delta = state.net.layers[index].delta; + float *delta = net.layers[index].delta; int input_size = l.input_sizes[i]; for(j = 0; j < l.batch; ++j){ axpy_cpu(input_size, 1, l.delta + offset + j*l.outputs, 1, delta + j*input_size, 1); @@ -101,31 +102,31 @@ void backward_route_layer(const route_layer l, network_state state) } #ifdef GPU -void forward_route_layer_gpu(const route_layer l, network_state state) +void forward_route_layer_gpu(const route_layer l, network net) { int i, j; int offset = 0; for(i = 0; i < l.n; ++i){ int index = l.input_layers[i]; - float *input = state.net.layers[index].output_gpu; + float *input = net.layers[index].output_gpu; int input_size = l.input_sizes[i]; for(j = 0; j < l.batch; ++j){ - copy_ongpu(input_size, input + j*input_size, 1, l.output_gpu + offset + j*l.outputs, 1); + copy_gpu(input_size, input + j*input_size, 1, l.output_gpu + offset + j*l.outputs, 1); } offset += input_size; } } -void backward_route_layer_gpu(const route_layer l, network_state state) +void backward_route_layer_gpu(const route_layer l, network net) { int i, j; int offset = 0; for(i = 0; i < l.n; ++i){ int index = l.input_layers[i]; - float *delta = state.net.layers[index].delta_gpu; + float *delta = net.layers[index].delta_gpu; int input_size = l.input_sizes[i]; for(j = 0; j < l.batch; ++j){ - axpy_ongpu(input_size, 1, l.delta_gpu + offset + j*l.outputs, 1, delta + j*input_size, 1); + axpy_gpu(input_size, 1, l.delta_gpu + offset + j*l.outputs, 1, delta + j*input_size, 1); } offset += input_size; } diff --git a/image.darknet/src/route_layer.h b/image.darknet/src/route_layer.h index 45467d9..1d40330 100644 --- a/image.darknet/src/route_layer.h +++ b/image.darknet/src/route_layer.h @@ -6,13 +6,13 @@ typedef layer route_layer; route_layer make_route_layer(int batch, int n, int *input_layers, int *input_size); -void forward_route_layer(const route_layer l, network_state state); -void backward_route_layer(const route_layer l, network_state state); +void forward_route_layer(const route_layer l, network net); +void backward_route_layer(const route_layer l, network net); void resize_route_layer(route_layer *l, network *net); #ifdef GPU -void forward_route_layer_gpu(const route_layer l, network_state state); -void backward_route_layer_gpu(const route_layer l, network_state state); +void forward_route_layer_gpu(const route_layer l, network net); +void backward_route_layer_gpu(const route_layer l, network net); #endif #endif diff --git a/image.darknet/src/shortcut_layer.c b/image.darknet/src/shortcut_layer.c index 8bca50f..49d17f5 100644 --- a/image.darknet/src/shortcut_layer.c +++ b/image.darknet/src/shortcut_layer.c @@ -1,12 +1,14 @@ #include "shortcut_layer.h" #include "cuda.h" #include "blas.h" +#include "activations.h" + #include #include layer make_shortcut_layer(int batch, int index, int w, int h, int c, int w2, int h2, int c2) { - fprintf(stderr,"Shortcut Layer: %d\n", index); + fprintf(stderr, "res %3d %4d x%4d x%4d -> %4d x%4d x%4d\n",index, w2,h2,c2, w,h,c); layer l = {0}; l.type = SHORTCUT; l.batch = batch; @@ -36,32 +38,53 @@ layer make_shortcut_layer(int batch, int index, int w, int h, int c, int w2, int return l; } -void forward_shortcut_layer(const layer l, network_state state) +void resize_shortcut_layer(layer *l, int w, int h) +{ + assert(l->w == l->out_w); + assert(l->h == l->out_h); + l->w = l->out_w = w; + l->h = l->out_h = h; + l->outputs = w*h*l->out_c; + l->inputs = l->outputs; + l->delta = realloc(l->delta, l->outputs*l->batch*sizeof(float)); + l->output = realloc(l->output, l->outputs*l->batch*sizeof(float)); + +#ifdef GPU + cuda_free(l->output_gpu); + cuda_free(l->delta_gpu); + l->output_gpu = cuda_make_array(l->output, l->outputs*l->batch); + l->delta_gpu = cuda_make_array(l->delta, l->outputs*l->batch); +#endif + +} + + +void forward_shortcut_layer(const layer l, network net) { - copy_cpu(l.outputs*l.batch, state.input, 1, l.output, 1); - shortcut_cpu(l.batch, l.w, l.h, l.c, state.net.layers[l.index].output, l.out_w, l.out_h, l.out_c, l.output); + copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); + shortcut_cpu(l.batch, l.w, l.h, l.c, net.layers[l.index].output, l.out_w, l.out_h, l.out_c, l.alpha, l.beta, l.output); activate_array(l.output, l.outputs*l.batch, l.activation); } -void backward_shortcut_layer(const layer l, network_state state) +void backward_shortcut_layer(const layer l, network net) { gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); - axpy_cpu(l.outputs*l.batch, 1, l.delta, 1, state.delta, 1); - shortcut_cpu(l.batch, l.out_w, l.out_h, l.out_c, l.delta, l.w, l.h, l.c, state.net.layers[l.index].delta); + axpy_cpu(l.outputs*l.batch, l.alpha, l.delta, 1, net.delta, 1); + shortcut_cpu(l.batch, l.out_w, l.out_h, l.out_c, l.delta, l.w, l.h, l.c, 1, l.beta, net.layers[l.index].delta); } #ifdef GPU -void forward_shortcut_layer_gpu(const layer l, network_state state) +void forward_shortcut_layer_gpu(const layer l, network net) { - copy_ongpu(l.outputs*l.batch, state.input, 1, l.output_gpu, 1); - shortcut_gpu(l.batch, l.w, l.h, l.c, state.net.layers[l.index].output_gpu, l.out_w, l.out_h, l.out_c, l.output_gpu); - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + shortcut_gpu(l.batch, l.w, l.h, l.c, net.layers[l.index].output_gpu, l.out_w, l.out_h, l.out_c, l.alpha, l.beta, l.output_gpu); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); } -void backward_shortcut_layer_gpu(const layer l, network_state state) +void backward_shortcut_layer_gpu(const layer l, network net) { - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); - axpy_ongpu(l.outputs*l.batch, 1, l.delta_gpu, 1, state.delta, 1); - shortcut_gpu(l.batch, l.out_w, l.out_h, l.out_c, l.delta_gpu, l.w, l.h, l.c, state.net.layers[l.index].delta_gpu); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + axpy_gpu(l.outputs*l.batch, l.alpha, l.delta_gpu, 1, net.delta_gpu, 1); + shortcut_gpu(l.batch, l.out_w, l.out_h, l.out_c, l.delta_gpu, l.w, l.h, l.c, 1, l.beta, net.layers[l.index].delta_gpu); } #endif diff --git a/image.darknet/src/shortcut_layer.h b/image.darknet/src/shortcut_layer.h index c09a809..5f684fc 100644 --- a/image.darknet/src/shortcut_layer.h +++ b/image.darknet/src/shortcut_layer.h @@ -5,12 +5,13 @@ #include "network.h" layer make_shortcut_layer(int batch, int index, int w, int h, int c, int w2, int h2, int c2); -void forward_shortcut_layer(const layer l, network_state state); -void backward_shortcut_layer(const layer l, network_state state); +void forward_shortcut_layer(const layer l, network net); +void backward_shortcut_layer(const layer l, network net); +void resize_shortcut_layer(layer *l, int w, int h); #ifdef GPU -void forward_shortcut_layer_gpu(const layer l, network_state state); -void backward_shortcut_layer_gpu(const layer l, network_state state); +void forward_shortcut_layer_gpu(const layer l, network net); +void backward_shortcut_layer_gpu(const layer l, network net); #endif #endif diff --git a/image.darknet/src/softmax_layer.c b/image.darknet/src/softmax_layer.c index 5d15314..9cbc6be 100644 --- a/image.darknet/src/softmax_layer.c +++ b/image.darknet/src/softmax_layer.c @@ -1,6 +1,7 @@ #include "softmax_layer.h" #include "blas.h" #include "cuda.h" + #include #include #include @@ -17,8 +18,10 @@ softmax_layer make_softmax_layer(int batch, int inputs, int groups) l.groups = groups; l.inputs = inputs; l.outputs = inputs; + l.loss = calloc(inputs*batch, sizeof(float)); l.output = calloc(inputs*batch, sizeof(float)); l.delta = calloc(inputs*batch, sizeof(float)); + l.cost = calloc(1, sizeof(float)); l.forward = forward_softmax_layer; l.backward = backward_softmax_layer; @@ -27,45 +30,35 @@ softmax_layer make_softmax_layer(int batch, int inputs, int groups) l.backward_gpu = backward_softmax_layer_gpu; l.output_gpu = cuda_make_array(l.output, inputs*batch); + l.loss_gpu = cuda_make_array(l.loss, inputs*batch); l.delta_gpu = cuda_make_array(l.delta, inputs*batch); #endif return l; } -void softmax_tree(float *input, int batch, int inputs, float temp, tree *hierarchy, float *output) +void forward_softmax_layer(const softmax_layer l, network net) { - int b; - for(b = 0; b < batch; ++b){ + if(l.softmax_tree){ int i; int count = 0; - for(i = 0; i < hierarchy->groups; ++i){ - int group_size = hierarchy->group_size[i]; - softmax(input+b*inputs + count, group_size, temp, output+b*inputs + count); + for (i = 0; i < l.softmax_tree->groups; ++i) { + int group_size = l.softmax_tree->group_size[i]; + softmax_cpu(net.input + count, group_size, l.batch, l.inputs, 1, 0, 1, l.temperature, l.output + count); count += group_size; } + } else { + softmax_cpu(net.input, l.inputs/l.groups, l.batch, l.inputs, l.groups, l.inputs/l.groups, 1, l.temperature, l.output); } -} -void forward_softmax_layer(const softmax_layer l, network_state state) -{ - int b; - int inputs = l.inputs / l.groups; - int batch = l.batch * l.groups; - if(l.softmax_tree){ - softmax_tree(state.input, batch, inputs, l.temperature, l.softmax_tree, l.output); - } else { - for(b = 0; b < batch; ++b){ - softmax(state.input+b*inputs, inputs, l.temperature, l.output+b*inputs); - } + if(net.truth && !l.noloss){ + softmax_x_ent_cpu(l.batch*l.inputs, l.output, net.truth, l.delta, l.loss); + l.cost[0] = sum_array(l.loss, l.batch*l.inputs); } } -void backward_softmax_layer(const softmax_layer l, network_state state) +void backward_softmax_layer(const softmax_layer l, network net) { - int i; - for(i = 0; i < l.inputs*l.batch; ++i){ - state.delta[i] += l.delta[i]; - } + axpy_cpu(l.inputs*l.batch, 1, l.delta, 1, net.delta, 1); } #ifdef GPU @@ -75,26 +68,40 @@ void pull_softmax_layer_output(const softmax_layer layer) cuda_pull_array(layer.output_gpu, layer.output, layer.inputs*layer.batch); } -void forward_softmax_layer_gpu(const softmax_layer l, network_state state) +void forward_softmax_layer_gpu(const softmax_layer l, network net) { - int inputs = l.inputs / l.groups; - int batch = l.batch * l.groups; if(l.softmax_tree){ + softmax_tree(net.input_gpu, 1, l.batch, l.inputs, l.temperature, l.output_gpu, *l.softmax_tree); + /* int i; int count = 0; for (i = 0; i < l.softmax_tree->groups; ++i) { int group_size = l.softmax_tree->group_size[i]; - softmax_gpu(state.input+count, group_size, inputs, batch, l.temperature, l.output_gpu + count); + softmax_gpu(net.input_gpu + count, group_size, l.batch, l.inputs, 1, 0, 1, l.temperature, l.output_gpu + count); count += group_size; } + */ } else { - softmax_gpu(state.input, inputs, inputs, batch, l.temperature, l.output_gpu); + if(l.spatial){ + softmax_gpu(net.input_gpu, l.c, l.batch*l.c, l.inputs/l.c, l.w*l.h, 1, l.w*l.h, 1, l.output_gpu); + }else{ + softmax_gpu(net.input_gpu, l.inputs/l.groups, l.batch, l.inputs, l.groups, l.inputs/l.groups, 1, l.temperature, l.output_gpu); + } + } + if(net.truth && !l.noloss){ + softmax_x_ent_gpu(l.batch*l.inputs, l.output_gpu, net.truth_gpu, l.delta_gpu, l.loss_gpu); + if(l.softmax_tree){ + mask_gpu(l.batch*l.inputs, l.delta_gpu, SECRET_NUM, net.truth_gpu, 0); + mask_gpu(l.batch*l.inputs, l.loss_gpu, SECRET_NUM, net.truth_gpu, 0); + } + cuda_pull_array(l.loss_gpu, l.loss, l.batch*l.inputs); + l.cost[0] = sum_array(l.loss, l.batch*l.inputs); } } -void backward_softmax_layer_gpu(const softmax_layer layer, network_state state) +void backward_softmax_layer_gpu(const softmax_layer layer, network net) { - axpy_ongpu(layer.batch*layer.inputs, 1, layer.delta_gpu, 1, state.delta, 1); + axpy_gpu(layer.batch*layer.inputs, 1, layer.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/src/softmax_layer.h b/image.darknet/src/softmax_layer.h index 821a8dd..2e3ffe0 100644 --- a/image.darknet/src/softmax_layer.h +++ b/image.darknet/src/softmax_layer.h @@ -7,13 +7,13 @@ typedef layer softmax_layer; void softmax_array(float *input, int n, float temp, float *output); softmax_layer make_softmax_layer(int batch, int inputs, int groups); -void forward_softmax_layer(const softmax_layer l, network_state state); -void backward_softmax_layer(const softmax_layer l, network_state state); +void forward_softmax_layer(const softmax_layer l, network net); +void backward_softmax_layer(const softmax_layer l, network net); #ifdef GPU void pull_softmax_layer_output(const softmax_layer l); -void forward_softmax_layer_gpu(const softmax_layer l, network_state state); -void backward_softmax_layer_gpu(const softmax_layer l, network_state state); +void forward_softmax_layer_gpu(const softmax_layer l, network net); +void backward_softmax_layer_gpu(const softmax_layer l, network net); #endif #endif diff --git a/image.darknet/src/stb_image.h b/image.darknet/src/stb_image.h index d0fa9c2..d9c21bc 100644 --- a/image.darknet/src/stb_image.h +++ b/image.darknet/src/stb_image.h @@ -1,5 +1,5 @@ -/* stb_image - v2.06 - public domain image loader - http://nothings.org/stb_image.h - no warranty implied; use at your own risk +/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION @@ -21,17 +21,20 @@ avoid problematic images and only need the trivial interface JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + PNG 1/2/4/8/16-bit-per-channel TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels) + PSD (composited view only, no extra channels, 8/16 bit-per-channel) GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) PNM (PPM and PGM binary only) + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - decode from arbitrary I/O callbacks - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) @@ -39,176 +42,65 @@ Full documentation under "DOCUMENTATION" below. - Revision 2.00 release notes: - - - Progressive JPEG is now supported. - - - PPM and PGM binary formats are now supported, thanks to Ken Miller. - - - x86 platforms now make use of SSE2 SIMD instructions for - JPEG decoding, and ARM platforms can use NEON SIMD if requested. - This work was done by Fabian "ryg" Giesen. SSE2 is used by - default, but NEON must be enabled explicitly; see docs. - - With other JPEG optimizations included in this version, we see - 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup - on a JPEG on an ARM machine, relative to previous versions of this - library. The same results will not obtain for all JPGs and for all - x86/ARM machines. (Note that progressive JPEGs are significantly - slower to decode than regular JPEGs.) This doesn't mean that this - is the fastest JPEG decoder in the land; rather, it brings it - closer to parity with standard libraries. If you want the fastest - decode, look elsewhere. (See "Philosophy" section of docs below.) - - See final bullet items below for more info on SIMD. - - - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing - the memory allocator. Unlike other STBI libraries, these macros don't - support a context parameter, so if you need to pass a context in to - the allocator, you'll have to store it in a global or a thread-local - variable. - - - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and - STBI_NO_LINEAR. - STBI_NO_HDR: suppress implementation of .hdr reader format - STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API - - - You can suppress implementation of any of the decoders to reduce - your code footprint by #defining one or more of the following - symbols before creating the implementation. - - STBI_NO_JPEG - STBI_NO_PNG - STBI_NO_BMP - STBI_NO_PSD - STBI_NO_TGA - STBI_NO_GIF - STBI_NO_HDR - STBI_NO_PIC - STBI_NO_PNM (.ppm and .pgm) - - - You can request *only* certain decoders and suppress all other ones - (this will be more forward-compatible, as addition of new decoders - doesn't require you to disable them explicitly): - - STBI_ONLY_JPEG - STBI_ONLY_PNG - STBI_ONLY_BMP - STBI_ONLY_PSD - STBI_ONLY_TGA - STBI_ONLY_GIF - STBI_ONLY_HDR - STBI_ONLY_PIC - STBI_ONLY_PNM (.ppm and .pgm) - - Note that you can define multiples of these, and you will get all - of them ("only x" and "only y" is interpreted to mean "only x&y"). - - - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still - want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB - - - Compilation of all SIMD code can be suppressed with - #define STBI_NO_SIMD - It should not be necessary to disable SIMD unless you have issues - compiling (e.g. using an x86 compiler which doesn't support SSE - intrinsics or that doesn't support the method used to detect - SSE2 support at run-time), and even those can be reported as - bugs so I can refine the built-in compile-time checking to be - smarter. - - - The old STBI_SIMD system which allowed installing a user-defined - IDCT etc. has been removed. If you need this, don't upgrade. My - assumption is that almost nobody was doing this, and those who - were will find the built-in SIMD more satisfactory anyway. - - - RGB values computed for JPEG images are slightly different from - previous versions of stb_image. (This is due to using less - integer precision in SIMD.) The C code has been adjusted so - that the same RGB values will be computed regardless of whether - SIMD support is available, so your app should always produce - consistent results. But these results are slightly different from - previous versions. (Specifically, about 3% of available YCbCr values - will compute different RGB results from pre-1.49 versions by +-1; - most of the deviating values are one smaller in the G channel.) - - - If you must produce consistent results with previous versions of - stb_image, #define STBI_JPEG_OLD and you will get the same results - you used to; however, you will not get the SIMD speedups for - the YCbCr-to-RGB conversion step (although you should still see - significant JPEG speedup from the other changes). - - Please note that STBI_JPEG_OLD is a temporary feature; it will be - removed in future versions of the library. It is only intended for - near-term back-compatibility use. - - - Latest revision history: - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) additional corruption checking - stbi_set_flip_vertically_on_load - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD - progressive JPEG - PGM/PPM support - STBI_MALLOC,STBI_REALLOC,STBI_FREE - STBI_NO_*, STBI_ONLY_* - GIF bugfix - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) - optimize PNG - fix bug in interlaced PNG with user-specified channel count +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED See end of file for full revision history. ============================ Contributors ========================= - Image formats Bug fixes & warning fixes - Sean Barrett (jpeg, png, bmp) Marc LeBlanc - Nicolas Schulz (hdr, psd) Christpher Lloyd - Jonathan Dummer (tga) Dave Moore - Jean-Marc Lienher (gif) Won Chun - Tom Seddon (pic) the Horde3D community - Thatcher Ulrich (psd) Janez Zemva - Ken Miller (pgm, ppm) Jonathan Blow - Laurent Gomila - Aruelien Pocheville - Extensions, features Ryamond Barbiero - Jetro Lauha (stbi_info) David Woo - Martin "SpartanJ" Golini (stbi_info) Martin Golini - James "moose2000" Brown (iPhone PNG) Roy Eltham - Ben "Disch" Wenger (io callbacks) Luke Graham - Omar Cornut (1/2/4-bit PNG) Thomas Ruf - Nicolas Guillemot (vertical flip) John Bartholomew - Ken Hamada - Optimizations & bugfixes Cort Stratton - Fabian "ryg" Giesen Blazej Dariusz Roszkowski - Arseny Kapoulkine Thibault Reuille - Paul Du Bois - Guillaume George - If your name should be here but Jerry Jansson - isn't, let Sean know. Hayaki Saito - Johan Duparc - Ronny Chevalier - Michal Cichon - Tero Hanninen - Sergio Gonzalez - Cass Everitt - Engin Manap - Martins Mozeiko - Joseph Thomson - Phil Jordan - -License: - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy - and modify this file however you want. - + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine + John-Mark Allen + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan + Dave Moore Roy Eltham Hayaki Saito Nathan Reed + Won Chun Luke Graham Johan Duparc Nick Verigakis + the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar + Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex + Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 + Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus + Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo + Christian Floisand Kevin Schmidt github:darealshinji + Blazej Dariusz Roszkowski github:Michaelangel007 */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -217,10 +109,8 @@ // DOCUMENTATION // // Limitations: -// - no 16-bit-per-channel PNG // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding -// - no 1-bit BMP // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): @@ -233,10 +123,10 @@ // stbi_image_free(data) // // Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *comp -- outputs # of image components in image file -// int req_comp -- if non-zero, # of image components requested in result +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points // to the pixel data, or NULL on an allocation failure or if the image is @@ -244,11 +134,12 @@ // with each pixel consisting of N interleaved 8-bit components; the first // pixel pointed to is top-left-most in the image. There is no padding between // image scanlines or between pixels, regardless of format. The number of -// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. -// If req_comp is non-zero, *comp has the number of components that _would_ -// have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp to see if it's trivially opaque -// because e.g. there were only 3 channels in the source image. +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. // // An output image with N components has the following components interleaved // in this order in each pixel: @@ -260,10 +151,10 @@ // 4 red, green, blue, alpha // // If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() -// can be queried for an extremely brief, end-user unfriendly explanation -// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid -// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly // more user-friendly ones. // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. @@ -282,13 +173,13 @@ // and for best performance I may provide less-easy-to-use APIs that give higher // performance, in addition to the easy to use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which // make more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") -// - Small footprint ("easy to maintain") +// - Small source code footprint ("easy to maintain") // - No dependencies ("ease of use") // // =========================================================================== @@ -320,13 +211,6 @@ // (at least this is true for iOS and Android). Therefore, the NEON support is // toggled by a build flag: define STBI_NEON to get NEON loops. // -// The output of the JPEG decoder is slightly different from versions where -// SIMD support was introduced (that is, for versions before 1.49). The -// difference is only +-1 in the 8-bit RGB channels, and only on a small -// fraction of pixels. You can force the pre-1.49 behavior by defining -// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path -// and hence cost some performance. -// // If for some reason you do not want to use any of SIMD code, or if // you have issues compiling it, you can disable it entirely by // defining STBI_NO_SIMD. @@ -382,6 +266,41 @@ // says there's premultiplied data (currently only happens in iPhone images, // and only if iPhone convert-to-rgb processing is on). // +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// #ifndef STBI_NO_STDIO @@ -392,7 +311,7 @@ enum { - STBI_default = 0, // only used for req_comp + STBI_default = 0, // only used for desired_channels STBI_grey = 1, STBI_grey_alpha = 2, @@ -401,6 +320,7 @@ enum }; typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; #ifdef __cplusplus extern "C" { @@ -428,34 +348,60 @@ typedef struct int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + #ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// #ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif #ifndef STBI_NO_HDR STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif +#endif // STBI_NO_HDR #ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_HDR +#endif // STBI_NO_LINEAR // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); @@ -476,11 +422,14 @@ STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); #endif @@ -561,9 +510,10 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #include // ptrdiff_t on osx #include #include +#include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp +#include // ldexp, pow #endif #ifndef STBI_NO_STDIO @@ -619,18 +569,22 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) #endif -#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC) +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) // ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) // ok #else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC." +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." #endif #ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,sz) realloc(p,sz) -#define STBI_FREE(p) free(p) +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) #endif // x86/x64 detection @@ -640,12 +594,14 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define STBI__X86_TARGET #endif -#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// NOTE: not clear do we actually need this for the 64-bit path? +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) // gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; -// this is just broken and gcc are jerks for not fixing it properly -// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. #define STBI_NO_SIMD #endif @@ -664,7 +620,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define STBI_NO_SIMD #endif -#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) #define STBI_SSE2 #include @@ -693,7 +649,7 @@ static int stbi__cpuid3(void) #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name -static int stbi__sse2_available() +static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; @@ -701,16 +657,12 @@ static int stbi__sse2_available() #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -static int stbi__sse2_available() +static int stbi__sse2_available(void) { -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later - // GCC 4.8+ has a nice way to do this - return __builtin_cpu_supports("sse2"); -#else - // portable way to do this, preferably without using GCC inline ASM? - // just bail for now. - return 0; -#endif + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; } #endif #endif @@ -749,7 +701,7 @@ typedef struct stbi_uc buffer_start[128]; stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original; + stbi_uc *img_buffer_original, *img_buffer_original_end; } stbi__context; @@ -761,7 +713,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) s->io.read = NULL; s->read_from_callbacks = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = (stbi_uc *) buffer+len; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } // initialize a callback-based context @@ -773,6 +725,7 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void * s->read_from_callbacks = 1; s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; } #ifndef STBI_NO_STDIO @@ -814,59 +767,76 @@ static void stbi__rewind(stbi__context *s) // but we just rewind to the beginning of the initial buffer, because // we only use it after doing 'test', which only ever looks at at most 92 bytes s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; } +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); -static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG static int stbi__png_test(stbi__context *s); -static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); #endif #ifndef STBI_NO_BMP static int stbi__bmp_test(stbi__context *s); -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA static int stbi__tga_test(stbi__context *s); -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s); -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); #endif #ifndef STBI_NO_HDR static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC static int stbi__pic_test(stbi__context *s); -static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif @@ -889,6 +859,81 @@ static void *stbi__malloc(size_t size) return STBI_MALLOC(size); } +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -901,8 +946,8 @@ static void *stbi__malloc(size_t size) #define stbi__err(x,y) stbi__err(x) #endif -#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) STBIDEF void stbi_image_free(void *retval_from_stbi_load) { @@ -924,33 +969,38 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) stbi__vertically_flip_on_load = flag_true_if_should_flip; } -static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); #endif #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif @@ -958,58 +1008,138 @@ static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *com #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp); + return stbi__tga_load(s,x,y,comp,req_comp, ri); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } -static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { - unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + int i; + int img_len = w * h * channels; + stbi_uc *reduced; - if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - stbi_uc temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; } } +} - return result; +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; } +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - float temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } - } + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); } } - +#endif #ifndef STBI_NO_STDIO @@ -1041,28 +1171,83 @@ STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req unsigned char *result; stbi__context s; stbi__start_file(&s,f); - result = stbi__load_flip(&s,x,y,comp,req_comp); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + #endif //!STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); - return stbi__load_flip(&s,x,y,comp,req_comp); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_flip(&s,x,y,comp,req_comp); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; } +#endif #ifndef STBI_NO_LINEAR static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) @@ -1070,13 +1255,14 @@ static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); if (hdr_data) stbi__float_postprocess(hdr_data,x,y,comp,req_comp); return hdr_data; } #endif - data = stbi__load_flip(s, x, y, comp, req_comp); + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); @@ -1146,13 +1332,18 @@ STBIDEF int stbi_is_hdr (char const *filename) return result; } -STBIDEF int stbi_is_hdr_from_file(FILE *f) +STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; stbi__context s; stbi__start_file(&s,f); - return stbi__hdr_test(&s); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; #else + STBI_NOTUSED(f); return 0; #endif } @@ -1165,18 +1356,21 @@ STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__hdr_test(&s); #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); return 0; #endif } -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; +#ifndef STBI_NO_LINEAR static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; -#ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } #endif +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } @@ -1285,17 +1479,23 @@ static stbi__uint32 stbi__get32be(stbi__context *s) return (z << 16) + stbi__get16be(s); } +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else static int stbi__get16le(stbi__context *s) { int z = stbi__get8(s); return z + (stbi__get8(s) << 8); } +#endif +#ifndef STBI_NO_BMP static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); return z + (stbi__get16le(s) << 16); } +#endif #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings @@ -1324,7 +1524,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - good = (unsigned char *) stbi__malloc(req_comp * x * y); + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); if (good == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); @@ -1334,26 +1534,75 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r unsigned char *src = data + j * x * img_n ; unsigned char *dest = good + j * x * req_comp; - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; default: STBI_ASSERT(0); } - #undef CASE + #undef STBI__CASE } STBI_FREE(data); @@ -1364,7 +1613,9 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { int i,k,n; - float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -1384,7 +1635,9 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { int i,k,n; - stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -1449,7 +1702,7 @@ typedef struct stbi__context *s; stbi__huffman huff_dc[4]; stbi__huffman huff_ac[4]; - stbi_uc dequant[4][64]; + stbi__uint16 dequant[4][64]; stbi__int16 fast_ac[4][1 << FAST_BITS]; // sizes for components, interleaved MCUs @@ -1485,6 +1738,9 @@ typedef struct int succ_high; int succ_low; int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; int scan_n, order[4]; int restart_interval, todo; @@ -1497,7 +1753,8 @@ typedef struct static int stbi__build_huffman(stbi__huffman *h, int *count) { - int i,j,k=0,code; + int i,j,k=0; + unsigned int code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) for (j=0; j < count[i]; ++j) @@ -1513,7 +1770,7 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) if (h->size[k] == j) { while (h->size[k] == j) h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); } // compute largest code + 1 for this size, preshifted as needed later h->maxcode[j] = code << (16-j); @@ -1554,10 +1811,10 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) // magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); int m = 1 << (magbits - 1); - if (k < m) k += (-1 << magbits) + 1; + if (k < m) k += (~0U << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); } } } @@ -1566,9 +1823,10 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { - int b = j->nomore ? 0 : stbi__get8(j->s); + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes if (c != 0) { j->marker = (unsigned char) c; j->nomore = 1; @@ -1581,7 +1839,7 @@ static void stbi__grow_buffer_unsafe(stbi__jpeg *j) } // (1 << n) - 1 -static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) @@ -1634,7 +1892,7 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) } // bias[n] = (-1<s); if (x != 0xff) return STBI__MARKER_none; while (x == 0xff) - x = stbi__get8(j->s); + x = stbi__get8(j->s); // consume repeated 0xff fill bytes return x; } @@ -2417,7 +2675,7 @@ static void stbi__jpeg_reset(stbi__jpeg *j) j->code_bits = 0; j->code_buffer = 0; j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; j->marker = STBI__MARKER_none; j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; j->eob_run = 0; @@ -2549,7 +2807,7 @@ static int stbi__parse_entropy_coded_data(stbi__jpeg *z) } } -static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) { int i; for (i=0; i < 64; ++i) @@ -2591,13 +2849,14 @@ static int stbi__process_marker(stbi__jpeg *z, int m) L = stbi__get16be(z->s)-2; while (L > 0) { int q = stbi__get8(z->s); - int p = q >> 4; + int p = q >> 4, sixteen = (p != 0); int t = q & 15,i; - if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); - L -= 65; + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); } return L==0; @@ -2630,12 +2889,50 @@ static int stbi__process_marker(stbi__jpeg *z, int m) } return L==0; } + // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - stbi__skip(z->s, stbi__get16be(z->s)-2); + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); return 1; } - return 0; + + return stbi__err("unknown marker","Corrupt JPEG"); } // after we see SOS @@ -2678,6 +2975,28 @@ static int stbi__process_scan_header(stbi__jpeg *z) return 1; } +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; @@ -2687,7 +3006,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires c = stbi__get8(s); - if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; for (i=0; i < c; ++i) { z->img_comp[i].data = NULL; @@ -2696,11 +3015,12 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + z->rgb = 0; for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! - return stbi__err("bad component ID","Corrupt JPEG"); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; q = stbi__get8(s); z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); @@ -2709,7 +3029,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (scan != STBI__SCAN_load) return 1; - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; @@ -2721,6 +3041,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) z->img_v_max = v_max; z->img_mcu_w = h_max * 8; z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; @@ -2732,28 +3053,27 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].data = NULL; - } - return stbi__err("outofmem", "Out of memory"); - } + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); // align blocks for idct using mmx/sse z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; if (z->progressive) { - z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; - z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; - z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } else { - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; } } @@ -2772,6 +3092,8 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 z->marker = STBI__MARKER_none; // initialize cached marker to empty m = stbi__get_marker(z); if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); @@ -2813,12 +3135,15 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) if (x == 255) { j->marker = stbi__get8(j->s); break; - } else if (x != 0) { - return stbi__err("junk before marker", "Corrupt JPEG"); } } // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); } else { if (!stbi__process_marker(j, m)) return 0; } @@ -3037,38 +3362,9 @@ static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_ return out; } -#ifdef STBI_JPEG_OLD -// this is the same YCbCr-to-RGB calculation that stb_image has used -// historically before the algorithm changes in 1.49 -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#else // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar -#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { int i; @@ -3077,9 +3373,9 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; @@ -3093,7 +3389,6 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc out += step; } } -#endif #if defined(STBI_SSE2) || defined(STBI_NEON) static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) @@ -3212,9 +3507,9 @@ static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc cons int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; @@ -3240,18 +3535,14 @@ static void stbi__setup_jpeg(stbi__jpeg *j) #ifdef STBI_SSE2 if (stbi__sse2_available()) { j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif #ifdef STBI_NEON j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } @@ -3259,23 +3550,7 @@ static void stbi__setup_jpeg(stbi__jpeg *j) // clean up the temporary component buffers static void stbi__cleanup_jpeg(stbi__jpeg *j) { - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].raw_data) { - STBI_FREE(j->img_comp[i].raw_data); - j->img_comp[i].raw_data = NULL; - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].raw_coeff) { - STBI_FREE(j->img_comp[i].raw_coeff); - j->img_comp[i].raw_coeff = 0; - j->img_comp[i].coeff = 0; - } - if (j->img_comp[i].linebuf) { - STBI_FREE(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } + stbi__free_jpeg_components(j, j->s->img_n, 0); } typedef struct @@ -3288,9 +3563,16 @@ typedef struct int ypos; // which pre-expansion row we're on } stbi__resample; +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { - int n, decode_n; + int n, decode_n, is_rgb; z->s->img_n = 0; // make stbi__cleanup_jpeg safe // validate req_comp @@ -3300,9 +3582,11 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n; + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - if (z->s->img_n == 3 && n < 3) + if (z->s->img_n == 3 && n < 3 && !is_rgb) decode_n = 1; else decode_n = z->s->img_n; @@ -3339,7 +3623,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp } // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } // now go ahead and resample @@ -3362,7 +3646,39 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (n >= 3) { stbi_uc *y = coutput[0]; if (z->s->img_n == 3) { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } } else for (i=0; i < z->s->img_x; ++i) { out[0] = out[1] = out[2] = y[i]; @@ -3370,37 +3686,70 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp out += n; } } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } } } stbi__cleanup_jpeg(z); *out_x = z->s->img_x; *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n; // report original components, not output + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output return output; } } -static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { - stbi__jpeg j; - j.s = s; - stbi__setup_jpeg(&j); - return load_jpeg_image(&j, x,y,comp,req_comp); + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; } static int stbi__jpeg_test(stbi__context *s) { int r; - stbi__jpeg j; - j.s = s; - stbi__setup_jpeg(&j); - r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); stbi__rewind(s); + STBI_FREE(j); return r; } @@ -3412,15 +3761,18 @@ static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) } if (x) *x = j->s->img_x; if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; return 1; } static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { - stbi__jpeg j; - j.s = s; - return stbi__jpeg_info_raw(&j, x, y, comp); + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; } #endif @@ -3466,7 +3818,7 @@ stbi_inline static int stbi__bit_reverse(int v, int bits) return stbi__bitreverse16(v) >> (16-bits); } -static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) { int i,k=0; int code, next_code[16], sizes[17]; @@ -3501,10 +3853,10 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) z->size [c] = (stbi_uc ) s; z->value[c] = (stbi__uint16) i; if (s <= STBI__ZFAST_BITS) { - int k = stbi__bit_reverse(next_code[s],s); - while (k < (1 << STBI__ZFAST_BITS)) { - z->fast[k] = fastv; - k += (1 << s); + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); } } ++next_code[s]; @@ -3543,7 +3895,7 @@ static void stbi__fill_bits(stbi__zbuf *z) { do { STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= stbi__zget8(z) << z->num_bits; + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); } @@ -3593,14 +3945,15 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; - int cur, limit; + int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); cur = (int) (z->zout - z->zout_start); - limit = (int) (z->zout_end - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); while (cur + n > limit) limit *= 2; - q = (char *) STBI_REALLOC(z->zout_start, limit); + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); z->zout_start = q; z->zout = q + cur; @@ -3608,18 +3961,18 @@ static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room return 1; } -static int stbi__zlength_base[31] = { +static const int stbi__zlength_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; -static int stbi__zlength_extra[31]= +static const int stbi__zlength_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; -static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; -static int stbi__zdist_extra[32] = +static const int stbi__zdist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static int stbi__parse_huffman_block(stbi__zbuf *a) @@ -3666,7 +4019,7 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) static int stbi__compute_huffman_codes(stbi__zbuf *a) { - static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; stbi__zhuffman z_codelength; stbi_uc lencodes[286+32+137];//padding for maximum single op stbi_uc codelength_sizes[19]; @@ -3675,6 +4028,7 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) int hlit = stbi__zreceive(a,5) + 257; int hdist = stbi__zreceive(a,5) + 1; int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { @@ -3684,33 +4038,35 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; - while (n < hlit + hdist) { + while (n < ntot) { int c = stbi__zhuffman_decode(a, &z_codelength); if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; - else if (c == 16) { - c = stbi__zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - memset(lencodes+n, 0, c); + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); n += c; } } - if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; } -static int stbi__parse_uncomperssed_block(stbi__zbuf *a) +static int stbi__parse_uncompressed_block(stbi__zbuf *a) { stbi_uc header[4]; int len,nlen,k; @@ -3752,9 +4108,24 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) return 1; } -// @TODO: should statically initialize these for optimal thread safety -static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; -static void stbi__init_zdefaults(void) +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: { int i; // use <= to match clearly with spec for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; @@ -3764,6 +4135,7 @@ static void stbi__init_zdefaults(void) for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; } +*/ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { @@ -3776,13 +4148,12 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); if (type == 0) { - if (!stbi__parse_uncomperssed_block(a)) return 0; + if (!stbi__parse_uncompressed_block(a)) return 0; } else if (type == 3) { return 0; } else { if (type == 1) { // use fixed code lengths - if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { @@ -3907,7 +4278,7 @@ static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) static int stbi__check_png_header(stbi__context *s) { - static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); @@ -3918,6 +4289,7 @@ typedef struct { stbi__context *s; stbi_uc *idata, *expanded, *out; + int depth; } stbi__png; @@ -3952,35 +4324,40 @@ static int stbi__paeth(int a, int b, int c) return c; } -static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { + int bytes = (depth == 16? 2 : 1); stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n; + stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; int k; int img_n = s->img_n; // copy it into a local for later + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } else { // interlaced: - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; - stbi_uc *prior = cur - stride; + stbi_uc *prior; int filter = *raw++; - int filter_bytes = img_n; - int width = x; + if (filter > 4) return stbi__err("invalid filter","Corrupt PNG"); @@ -3990,6 +4367,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r filter_bytes = 1; width = img_width_bytes; } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; @@ -4013,6 +4391,14 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r raw += img_n; cur += out_n; prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; } else { raw += 1; cur += 1; @@ -4021,38 +4407,47 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r // this is a little gross, so that we don't switch per-pixel or per-component if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*img_n; - #define CASE(f) \ + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ case f: \ for (k=0; k < nk; ++k) switch (filter) { // "none" filter turns into a memcpy here; make that explicit. case STBI__F_none: memcpy(cur, raw, nk); break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; } - #undef CASE + #undef STBI__CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); - #define CASE(f) \ + #define STBI__CASE(f) \ case f: \ - for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ - for (k=0; k < img_n; ++k) + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) switch (filter) { - CASE(STBI__F_none) cur[k] = raw[k]; break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } } - #undef CASE } } @@ -4109,25 +4504,36 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } if (img_n != out_n) { + int q; // insert alpha = 255 - stbi_uc *cur = a->out + stride*j; - int i; + cur = a->out + stride*j; if (img_n == 1) { - for (i=x-1; i >= 0; --i) { - cur[i*2+1] = 255; - cur[i*2+0] = cur[i]; + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; } } else { STBI_ASSERT(img_n == 3); - for (i=x-1; i >= 0; --i) { - cur[i*4+3] = 255; - cur[i*4+2] = cur[i*3+2]; - cur[i*4+1] = cur[i*3+1]; - cur[i*4+0] = cur[i*3+0]; + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; } } } } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } } return 1; @@ -4135,13 +4541,15 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; stbi_uc *final; int p; if (!interlaced) return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing - final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; @@ -4161,8 +4569,8 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 for (i=0; i < x; ++i) { int out_y = j*yspc[p]+yorig[p]; int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, - a->out + (j*x+i)*out_n, out_n); + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); } } STBI_FREE(a->out); @@ -4200,12 +4608,37 @@ static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) return 1; } +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) { stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; stbi_uc *p, *temp_out, *orig = a->out; - p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); if (p == NULL) return stbi__err("outofmem", "Out of memory"); // between here and free(out) below, exitting would leak @@ -4271,9 +4704,10 @@ static void stbi__de_iphone(stbi__png *z) stbi_uc a = p[3]; stbi_uc t = p[0]; if (a) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / a; + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; } else { p[0] = p[2]; p[2] = t; @@ -4292,14 +4726,15 @@ static void stbi__de_iphone(stbi__png *z) } } -#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; stbi_uc has_trans=0, tc[3]; + stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; + int first=1,k,interlace=0, color=0, is_iphone=0; stbi__context *s = z->s; z->expanded = NULL; @@ -4324,8 +4759,9 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); @@ -4373,8 +4809,11 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; - for (k=0; k < s->img_n; ++k) - tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } } break; } @@ -4385,11 +4824,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; stbi_uc *p; if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; while (ioff + c.length > idata_limit) idata_limit *= 2; - p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); z->idata = p; } if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); @@ -4403,7 +4844,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (scan != STBI__SCAN_load) return 1; if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); if (z->expanded == NULL) return 0; // zlib should set error @@ -4412,9 +4853,14 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) s->img_out_n = s->img_n+1; else s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; - if (has_trans) - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) stbi__de_iphone(z); if (pal_img_n) { @@ -4424,6 +4870,9 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (req_comp >= 3) s->img_out_n = req_comp; if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; return 1; @@ -4451,21 +4900,28 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) } } -static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { - unsigned char *result=NULL; + void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { - result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); p->s->img_out_n = req_comp; if (result == NULL) return result; } *x = p->s->img_x; *y = p->s->img_y; - if (n) *n = p->s->img_out_n; + if (n) *n = p->s->img_n; } STBI_FREE(p->out); p->out = NULL; STBI_FREE(p->expanded); p->expanded = NULL; @@ -4474,11 +4930,11 @@ static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req return result; } -static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi__png p; p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp); + return stbi__do_png(&p, x,y,comp,req_comp, ri); } static int stbi__png_test(stbi__context *s) @@ -4507,6 +4963,19 @@ static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) p.s = s; return stbi__png_info_raw(&p, x, y, comp); } + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} #endif // Microsoft/Windows BMP image @@ -4558,36 +5027,46 @@ static int stbi__bitcount(unsigned int a) return a & 0xff; } +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. static int stbi__shiftsigned(int v, int shift, int bits) { - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v >= 0 && v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; } -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +typedef struct { - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; - stbi_uc pal[256][4]; - int psize=0,i,j,compress=0,width; - int bpp, flip_vertically, pad, target, offset, hsz; + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved - offset = stbi__get32le(s); - hsz = stbi__get32le(s); + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { s->img_x = stbi__get16le(s); @@ -4597,15 +5076,9 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int s->img_y = stbi__get32le(s); } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - bpp = stbi__get16le(s); - if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - if (hsz == 12) { - if (bpp < 24) - psize = (offset - 14 - 24) / 3; - } else { - compress = stbi__get32le(s); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres @@ -4619,27 +5092,25 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int stbi__get32le(s); stbi__get32le(s); } - if (bpp == 16 || bpp == 32) { - mr = mg = mb = 0; + if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { - if (bpp == 32) { - mr = 0xffu << 16; - mg = 0xffu << 8; - mb = 0xffu << 0; - ma = 0xffu << 24; - fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 - STBI_NOTUSED(fake_a); + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 } else { - mr = 31u << 10; - mg = 31u << 5; - mb = 31u << 0; + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; } } else if (compress == 3) { - mr = stbi__get32le(s); - mg = stbi__get32le(s); - mb = stbi__get32le(s); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); // not documented, but generated by photoshop and handled by mspaint - if (mr == mg && mg == mb) { + if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? return stbi__errpuc("bad BMP", "bad BMP"); } @@ -4647,11 +5118,13 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int return stbi__errpuc("bad BMP", "bad BMP"); } } else { - STBI_ASSERT(hsz == 108 || hsz == 124); - mr = stbi__get32le(s); - mg = stbi__get32le(s); - mb = stbi__get32le(s); - ma = stbi__get32le(s); + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters @@ -4662,63 +5135,119 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int stbi__get32le(s); // discard reserved } } - if (bpp < 16) - psize = (offset - 14 - hsz) >> 2; } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else target = s->img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (bpp < 16) { + if (info.bpp < 16) { int z=0; if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } for (i=0; i < psize; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); - if (hsz != 12) stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } - stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); - if (bpp == 4) width = (s->img_x + 1) >> 1; - else if (bpp == 8) width = s->img_x; + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (bpp == 4) { - v2 = v & 15; - v >>= 4; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); } - stbi__skip(s, pad); } } else { int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; - stbi__skip(s, offset - 14 - hsz); - if (bpp == 24) width = 3 * s->img_x; - else if (bpp == 16) width = 2*s->img_x; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; pad = (-width) & 3; - if (bpp == 24) { + if (info.bpp == 24) { easy = 1; - } else if (bpp == 32) { + } else if (info.bpp == 32) { if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) easy = 2; } @@ -4739,22 +5268,31 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int out[z+0] = stbi__get8(s); z += 3; a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; if (target == 4) out[z++] = a; } } else { + int bpp = info.bpp; for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - int a; + unsigned int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; if (target == 4) out[z++] = STBI__BYTECAST(a); } } stbi__skip(s, pad); } } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + if (flip_vertically) { stbi_uc t; for (j=0; j < (int) s->img_y>>1; ++j) { @@ -4781,20 +5319,55 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int // Targa Truevision - TGA // by Jonathan Dummer #ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { - int tga_w, tga_h, tga_comp; - int sz; + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; stbi__get8(s); // discard Offset - sz = stbi__get8(s); // color type - if( sz > 1 ) { + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { stbi__rewind(s); return 0; // only RGB or indexed allowed } - sz = stbi__get8(s); // image type - // only RGB or grey allowed, +/- RLE - if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; - stbi__skip(s,9); + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } tga_w = stbi__get16le(s); if( tga_w < 1 ) { stbi__rewind(s); @@ -4805,45 +5378,81 @@ static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) stbi__rewind(s); return 0; // test height } - sz = stbi__get8(s); // bits per pixel - // only RGB or RGBA or grey allowed - if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { - stbi__rewind(s); - return 0; + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; } - tga_comp = sz; if (x) *x = tga_w; if (y) *y = tga_h; - if (comp) *comp = tga_comp / 8; + if (comp) *comp = tga_comp; return 1; // seems to have passed everything } static int stbi__tga_test(stbi__context *s) { - int res; - int sz; + int res = 0; + int sz, tga_color_type; stbi__get8(s); // discard Offset - sz = stbi__get8(s); // color type - if ( sz > 1 ) return 0; // only RGB or indexed allowed + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed sz = stbi__get8(s); // image type - if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE - stbi__get16be(s); // discard palette start - stbi__get16be(s); // discard palette length - stbi__get8(s); // discard bits per palette color entry - stbi__get16be(s); // discard x origin - stbi__get16be(s); // discard y origin - if ( stbi__get16be(s) < 1 ) return 0; // test width - if ( stbi__get16be(s) < 1 ) return 0; // test height + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height sz = stbi__get8(s); // bits per pixel - if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) - res = 0; - else - res = 1; + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: stbi__rewind(s); return res; } -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { // read in the TGA header stuff int tga_offset = stbi__get8(s); @@ -4858,16 +5467,18 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int int tga_width = stbi__get16le(s); int tga_height = stbi__get16le(s); int tga_bits_per_pixel = stbi__get8(s); - int tga_comp = tga_bits_per_pixel / 8; + int tga_comp, tga_rgb16=0; int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) // image data unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; - unsigned char raw_data[4]; + unsigned char raw_data[4] = {0}; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; + STBI_NOTUSED(ri); // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -4875,41 +5486,33 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int tga_image_type -= 8; tga_is_RLE = 1; } - /* int tga_alpha_bits = tga_inverted & 15; */ tga_inverted = 1 - ((tga_inverted >> 5) & 1); - // error check - if ( //(tga_indexed) || - (tga_width < 1) || (tga_height < 1) || - (tga_image_type < 1) || (tga_image_type > 3) || - ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && - (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) - ) - { - return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA - } - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) - { - tga_comp = tga_palette_bits / 8; - } + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); // tga info *x = tga_width; *y = tga_height; if (comp) *comp = tga_comp; - tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) stbi__skip(s, tga_offset ); - if ( !tga_indexed && !tga_is_RLE) { + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { for (i=0; i < tga_height; ++i) { - int y = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + y*tga_width*tga_comp; + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; stbi__getn(s, tga_row, tga_width * tga_comp); } } else { @@ -4919,15 +5522,22 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette - tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 ); + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); } - if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); } } // load the data @@ -4957,23 +5567,22 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int // load however much data we did have if ( tga_indexed ) { - // read in 1 byte, then perform the lookup - int pal_idx = stbi__get8(s); - if ( pal_idx >= tga_palette_len ) - { - // invalid index + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index pal_idx = 0; } - pal_idx *= tga_bits_per_pixel / 8; - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { raw_data[j] = tga_palette[pal_idx+j]; } - } else - { + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { // read in the data raw - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { + for (j = 0; j < tga_comp; ++j) { raw_data[j] = stbi__get8(s); } } @@ -5012,8 +5621,8 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int } } - // swap RGB - if (tga_comp >= 3) + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) { unsigned char* tga_pixel = tga_data; for (i=0; i < tga_width * tga_height; ++i) @@ -5049,13 +5658,53 @@ static int stbi__psd_test(stbi__context *s) return r; } -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { - int pixelCount; + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; int channelCount, compression; - int channel, i, count, len; + int channel, i; + int bitdepth; int w,h; stbi_uc *out; + STBI_NOTUSED(ri); // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" @@ -5078,8 +5727,9 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int w = stbi__get32be(s); // Make sure the depth is 8 bits. - if (stbi__get16be(s) != 8) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); // Make sure the color mode is RGB. // Valid options are: @@ -5111,8 +5761,18 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int if (compression > 1) return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + // Create the destination image. - out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; @@ -5144,61 +5804,86 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); } } } } else { // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out + channel; - if (channel > channelCount) { + if (channel >= channelCount) { // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = channel == 3 ? 255 : 0; + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } } else { - // Read the data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } } } } + // convert to desired output format if (req_comp && req_comp != 4) { - out = stbi__convert_format(out, 4, req_comp, w, h); + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); if (out == NULL) return out; // stbi__convert_format frees input on failure } @@ -5350,7 +6035,6 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c if (count >= 128) { // Repeated stbi_uc value[4]; - int i; if (count==128) count = stbi__get16be(s); @@ -5383,10 +6067,13 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c return result; } -static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) { stbi_uc *result; - int i, x,y; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; for (i=0; i<92; ++i) stbi__get8(s); @@ -5394,14 +6081,14 @@ static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int re x = stbi__get16be(s); y = stbi__get16be(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); stbi__get32be(s); //skip `ratio' stbi__get16be(s); //skip `fields' stbi__get16be(s); //skip `pad' // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc(x*y*4); + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { @@ -5439,10 +6126,12 @@ typedef struct { int w,h; stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; - stbi__gif_lzw codes[4096]; + stbi__gif_lzw codes[8192]; stbi_uc *color_table; int parse, step; int lflags; @@ -5450,6 +6139,7 @@ typedef struct int max_x, max_y; int cur_x, cur_y; int line_size; + int delay; } stbi__gif; static int stbi__gif_test_raw(stbi__context *s) @@ -5510,19 +6200,22 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { - stbi__gif g; - if (!stbi__gif_header(s, &g, comp, 1)) { + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); stbi__rewind( s ); return 0; } - if (x) *x = g.w; - if (y) *y = g.h; + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); return 1; } static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -5531,10 +6224,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; - if (c[3] >= 128) { + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -5557,7 +6252,7 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) { stbi_uc lzw_cs; - stbi__int32 len, code; + stbi__int32 len, init_code; stbi__uint32 first; stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; stbi__gif_lzw *p; @@ -5570,10 +6265,10 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) codemask = (1 << codesize) - 1; bits = 0; valid_bits = 0; - for (code = 0; code < clear; code++) { - g->codes[code].prefix = -1; - g->codes[code].first = (stbi_uc) code; - g->codes[code].suffix = (stbi_uc) code; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; } // support no starting clear code @@ -5608,11 +6303,16 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) stbi__skip(s,len); return g->out; } else if (code <= avail) { - if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } if (oldcode >= 0) { p = &g->codes[avail++]; - if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + p->prefix = (stbi__int16) oldcode; p->first = g->codes[oldcode].first; p->suffix = (code == avail) ? p->first : g->codes[code].first; @@ -5634,43 +6334,70 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) } } -static void stbi__fill_gif_background(stbi__gif *g) -{ - int i; - stbi_uc *c = g->pal[g->bgindex]; - // @OPTIMIZE: write a dword at a time - for (i = 0; i < g->w * g->h * 4; i += 4) { - stbi_uc *p = &g->out[i]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } -} - // this function is designed to support animated gifs, although stb_image doesn't support it -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { - int i; - stbi_uc *old_out = 0; + int dispose; + int first_frame; + int pi; + int pcount; + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; if (g->out == 0) { if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->history = (stbi_uc *) stbi__malloc(g->w * g->h); if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - stbi__fill_gif_background(g); + + // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to teh color that was there the previous frame. + memset( g->out, 0x00, 4 * g->w * g->h ); + memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + first_frame = 1; } else { - // animated-gif-only path - if (((g->eflags & 0x1C) >> 2) == 3) { - old_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - memcpy(g->out, old_out, g->w*g->h*4); + // second frame - how do we dispoase of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + for (;;) { - switch (stbi__get8(s)) { + int tag = stbi__get8(s); + switch (tag) { case 0x2C: /* Image Descriptor */ { stbi__int32 x, y, w, h; @@ -5705,38 +6432,60 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { - for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent - g->pal[i][3] = 255; - if (g->transparent >= 0 && (g->eflags & 0x01)) - g->pal[g->transparent][3] = 0; g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - + return stbi__errpuc("missing color table", "Corrupt GIF"); + o = stbi__process_gif_raster(s, g); if (o == NULL) return NULL; - if (req_comp && req_comp != 4) - o = stbi__convert_format(o, 4, req_comp, g->w, g->h); + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + return o; } case 0x21: // Comment Extension. { int len; - if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); - stbi__get16le(s); // delay - g->transparent = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } } else { stbi__skip(s, len); break; } - } - while ((len = stbi__get8(s)) != 0) + } + while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); + } break; } @@ -5749,19 +6498,90 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } } -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (delays) { + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); - u = stbi__gif_load_next(s, &g, comp, req_comp); + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + return u; } @@ -5775,20 +6595,24 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s) +static int stbi__hdr_test_core(stbi__context *s, const char *signature) { - const char *signature = "#?RADIANCE\n"; int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) - return 0; + return 0; + stbi__rewind(s); return 1; } static int stbi__hdr_test(stbi__context* s) { - int r = stbi__hdr_test_core(s); + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } return r; } @@ -5842,7 +6666,7 @@ static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) } } -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { char buffer[STBI__HDR_BUFLEN]; char *token; @@ -5853,10 +6677,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re int len; unsigned char count, value; int i, j, k, c1,c2, z; - + const char *headerToken; + STBI_NOTUSED(ri); // Check identifier - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) return stbi__errpf("not HDR", "Corrupt HDR image"); // Parse header @@ -5885,8 +6711,13 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re if (comp) *comp = 3; if (req_comp == 0) req_comp = 3; + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + // Read data - hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); // Load image data // image data is stored as some number of sca @@ -5925,20 +6756,29 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re len <<= 8; len |= stbi__get8(s); if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } for (k = 0; k < 4; ++k) { + int nleft; i = 0; - while (i < width) { + while ((nleft = width - i) > 0) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -5947,7 +6787,8 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re for (i=0; i < width; ++i) stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); } - STBI_FREE(scanline); + if (scanline) + STBI_FREE(scanline); } return hdr_data; @@ -5958,8 +6799,13 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + if (stbi__hdr_test(s) == 0) { stbi__rewind( s ); return 0; } @@ -5996,29 +6842,17 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_BMP static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) { - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') { - stbi__rewind( s ); - return 0; - } - stbi__skip(s,12); - hsz = stbi__get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) { - stbi__rewind( s ); - return 0; - } - if (hsz == 12) { - *x = stbi__get16le(s); - *y = stbi__get16le(s); - } else { - *x = stbi__get32le(s); - *y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) { - stbi__rewind( s ); - return 0; - } - *comp = stbi__get16le(s) / 8; + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) *comp = info.ma ? 4 : 3; return 1; } #endif @@ -6026,7 +6860,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { - int channelCount; + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; @@ -6043,7 +6880,8 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) } *y = stbi__get32be(s); *x = stbi__get32be(s); - if (stbi__get16be(s) != 8) { + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { stbi__rewind( s ); return 0; } @@ -6054,22 +6892,61 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) *comp = 4; return 1; } + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} #endif #ifndef STBI_NO_PIC static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { - int act_comp=0,num_packets=0,chained; + int act_comp=0,num_packets=0,chained,dummy; stbi__pic_packet packets[10]; - stbi__skip(s, 92); + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); *x = stbi__get16be(s); *y = stbi__get16be(s); - if (stbi__at_eof(s)) return 0; + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } stbi__skip(s, 8); @@ -6129,16 +7006,22 @@ static int stbi__pnm_test(stbi__context *s) return 1; } -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; + STBI_NOTUSED(ri); + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; + *x = s->img_x; *y = s->img_y; - *comp = s->img_n; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); - out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); stbi__getn(s, out, s->img_n * s->img_x * s->img_y); @@ -6156,8 +7039,16 @@ static int stbi__pnm_isspace(char c) static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } } static int stbi__pnm_isdigit(char c) @@ -6179,16 +7070,20 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { - int maxv; + int maxv, dummy; char c, p, t; - stbi__rewind( s ); + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); // Get identifier p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); + stbi__rewind(s); return 0; } @@ -6254,6 +7149,19 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + #ifndef STBI_NO_STDIO STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { @@ -6275,6 +7183,27 @@ STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) fseek(f,pos,SEEK_SET); return r; } + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} #endif // !STBI_NO_STDIO STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) @@ -6291,10 +7220,63 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int return stbi__info_main(&s,x,y,comp); } +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + #endif // STB_IMAGE_IMPLEMENTATION /* revision history: + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit @@ -6435,3 +7417,46 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int 0.50 (2006-11-19) first released version */ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/image.darknet/src/stb_image_write.h b/image.darknet/src/stb_image_write.h index f5250b3..c05e958 100644 --- a/image.darknet/src/stb_image_write.h +++ b/image.darknet/src/stb_image_write.h @@ -1,7 +1,6 @@ -/* stb_image_write - v0.98 - public domain - http://nothings.org/stb/stb_image_write.h - writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 - no warranty implied; use at your own risk - +/* stb_image_write - v1.09 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk Before #including, @@ -11,31 +10,67 @@ Will probably not work correctly with strict-aliasing optimizations. + If using a modern Microsoft Compiler, non-safe versions of CRT calls may cause + compilation warnings or even errors. To avoid this, also before #including, + + #define STBI_MSC_SECURE_CRT + ABOUT: This header file is a library for writing images to C stdio. It could be adapted to write to memory or a general streaming interface; let me know. The PNG output is not optimal; it is 20-50% larger than the file - written by a decent optimizing implementation. This library is designed - for source code compactness and simplicitly, not optimal image file size - or run-time performance. + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. - You can define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), USAGE: - There are four functions, one for each image file format: + There are five functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); - int stbi_write_hdr(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. Each function returns 0 on failure and non-0 on success. @@ -59,63 +94,138 @@ writer, both because it is in BGR order and because it may have padding at the end of the line.) + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + CREDITS: - PNG/BMP/TGA - Sean Barrett - HDR - Baldur Karlsson - TGA monochrome: - Jean-Sebastien Guay - misc enhancements: - Tim Kelsey + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + bugfixes: github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + +LICENSE + + See end of file for license information. + */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else #ifdef __cplusplus -extern "C" { +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif #endif -extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); -extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); -extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); -extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +extern int stbi_write_tga_with_rle; +extern int stbi_write_png_compression_level; +extern int stbi_write_force_png_filter; +#endif -#ifdef __cplusplus -} +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #endif +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + #endif//INCLUDE_STB_IMAGE_WRITE_H #ifdef STB_IMAGE_WRITE_IMPLEMENTATION +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + #include #include -#include #include #include -#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC) +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) // ok -#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) // ok #else -#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC." +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." #endif #ifndef STBIW_MALLOC -#define STBIW_MALLOC(sz) malloc(sz) -#define STBIW_REALLOC(p,sz) realloc(p,sz) -#define STBIW_FREE(p) free(p) +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) #endif + + #ifndef STBIW_MEMMOVE #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) #endif @@ -126,22 +236,90 @@ extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const fl #define STBIW_ASSERT(x) assert(x) #endif +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi__flip_vertically_on_write=0; +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi__flip_vertically_on_write=0; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f; +#ifdef STBI_MSC_SECURE_CRT + if (fopen_s(&f, filename, "wb")) + f = NULL; +#else + f = fopen(filename, "wb"); +#endif + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; -static void writefv(FILE *f, const char *fmt, va_list v) +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { switch (*fmt++) { case ' ': break; - case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; } - case '2': { int x = va_arg(v,int); unsigned char b[2]; - b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8); - fwrite(b,2,1,f); break; } - case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; - b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8); - b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24); - fwrite(b,4,1,f); break; } + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } default: STBIW_ASSERT(0); return; @@ -149,22 +327,70 @@ static void writefv(FILE *f, const char *fmt, va_list v) } } -static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c) +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { unsigned char arr[3]; arr[0] = a, arr[1] = b, arr[2] = c; - fwrite(arr, 3, 1, f); + s->func(s->context, arr, 3); } -static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) { unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ stbiw_uint32 zero = 0; - int i,j,k, j_end; + int i,j, j_end; if (y <= 0) return; + if (stbi__flip_vertically_on_write) + vdir *= -1; + if (vdir < 0) j_end = -1, j = y-1; else @@ -173,73 +399,157 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, for (; j != j_end; j += vdir) { for (i=0; i < x; ++i) { unsigned char *d = (unsigned char *) data + (j*x+i)*comp; - if (write_alpha < 0) - fwrite(&d[comp-1], 1, 1, f); - switch (comp) { - case 1: fwrite(d, 1, 1, f); - break; - case 2: if (expand_mono) - write3(f, d[0],d[0],d[0]); // monochrome bmp - else - fwrite(d, 1, 1, f); // monochrome TGA - break; - case 4: - if (!write_alpha) { - // composite against pink background - for (k=0; k < 3; ++k) - px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255; - write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]); - break; - } - /* FALLTHROUGH */ - case 3: - write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]); - break; - } - if (write_alpha > 0) - fwrite(&d[comp-1], 1, 1, f); + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } - fwrite(&zero,scanline_pad,1,f); + s->func(s->context, &zero, scanline_pad); } } -static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) { - FILE *f; - if (y < 0 || x < 0) return 0; - f = fopen(filename, "wb"); - if (f) { + if (y < 0 || x < 0) { + return 0; + } else { va_list v; va_start(v, fmt); - writefv(f, fmt, v); + stbiw__writefv(s, fmt, v); va_end(v); - write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad,expand_mono); - fclose(f); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; } - return f != NULL; } -int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { int pad = (-x*3) & 3; - return outfile(filename,-1,-1,x,y,comp,1,(void *) data,0,pad, + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, "11 4 22 4" "4 44 22 444444", 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header } -int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) { int has_alpha = (comp == 2 || comp == 4); int colorbytes = has_alpha ? comp-1 : comp; int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 - return outfile(filename, -1,-1, x, y, comp, 0, (void *) data, has_alpha, 0, - "111 221 2222 11", 0,0,format, 0,0,0, 0,0,x,y, (colorbytes+has_alpha)*8, has_alpha*8); + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; } +#endif // ************************************************************************************************* // Radiance RGBE HDR writer // by Baldur Karlsson + #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) @@ -247,7 +557,7 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) int exponent; float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); - if (maxcomp < 1e-32) { + if (maxcomp < 1e-32f) { rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; } else { float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; @@ -259,27 +569,27 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) } } -void stbiw__write_run_data(FILE *f, int length, unsigned char databyte) +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { - unsigned char lengthbyte = (unsigned char) (length+128); + unsigned char lengthbyte = STBIW_UCHAR(length+128); STBIW_ASSERT(length+128 <= 255); - fwrite(&lengthbyte, 1, 1, f); - fwrite(&databyte, 1, 1, f); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); } -void stbiw__write_dump_data(FILE *f, int length, unsigned char *data) +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { - unsigned char lengthbyte = (unsigned char )(length & 0xff); + unsigned char lengthbyte = STBIW_UCHAR(length); STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code - fwrite(&lengthbyte, 1, 1, f); - fwrite(data, length, 1, f); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); } -void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scratch, const float *scanline) +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char rgbe[4]; - float linear[3] = {0}; + float linear[3]; int x; scanlineheader[2] = (width&0xff00)>>8; @@ -288,31 +598,31 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra /* skip RLE for images too small or large */ if (width < 8 || width >= 32768) { for (x=0; x < width; x++) { - switch (comp) { + switch (ncomp) { case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*comp + 2]; - linear[1] = scanline[x*comp + 1]; - linear[0] = scanline[x*comp + 0]; + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; break; - case 2: /* fallthrough */ - case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0]; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); - fwrite(rgbe, 4, 1, f); + s->func(s->context, rgbe, 4); } } else { int c,r; /* encode into scratch buffer */ for (x=0; x < width; x++) { - switch(comp) { + switch(ncomp) { case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*comp + 2]; - linear[1] = scanline[x*comp + 1]; - linear[0] = scanline[x*comp + 0]; + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; break; - case 2: /* fallthrough */ - case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0]; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); @@ -322,7 +632,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra scratch[x + width*3] = rgbe[3]; } - fwrite(scanlineheader, 4, 1, f); + s->func(s->context, scanlineheader, 4); /* RLE each component separately */ for (c=0; c < 4; c++) { @@ -343,7 +653,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra while (x < r) { int len = r-x; if (len > 128) len = 128; - stbiw__write_dump_data(f, len, &comp[x]); + stbiw__write_dump_data(s, len, &comp[x]); x += len; } // if there's a run, output it @@ -355,7 +665,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra while (x < r) { int len = r-x; if (len > 127) len = 127; - stbiw__write_run_data(f, len, comp[x]); + stbiw__write_run_data(s, len, comp[x]); x += len; } } @@ -364,28 +674,59 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra } } -int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) { - int i; - FILE *f; - if (y <= 0 || x <= 0 || data == NULL) return 0; - f = fopen(filename, "wb"); - if (f) { - /* Each component is stored separately. Allocate scratch space for full output scanline. */ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); - fprintf(f, "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n" ); - fprintf(f, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n" , y, x); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef STBI_MSC_SECURE_CRT + len = sprintf_s(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + for(i=0; i < y; i++) - stbiw__write_hdr_scanline(f, x, comp, scratch, data + comp*i*x); + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)*x); STBIW_FREE(scratch); - fclose(f); + return 1; } - return f != NULL; } -///////////////////////////////////////////////////////// -// PNG +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #define stbiw__sbraw(a) ((int *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] @@ -402,7 +743,7 @@ int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *da static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; - void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); STBIW_ASSERT(p); if (p) { if (!*arr) ((int *) p)[1] = 0; @@ -415,7 +756,7 @@ static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { while (*bitcount >= 8) { - stbiw__sbpush(data, (unsigned char) *bitbuffer); + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); *bitbuffer >>= 8; *bitcount -= 8; } @@ -466,8 +807,14 @@ static unsigned int stbiw__zhash(unsigned char *data) #define stbiw__ZHASH 16384 +#endif // STBIW_ZLIB_COMPRESS + unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; @@ -475,7 +822,9 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; - unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack! + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (hash_table == NULL) + return NULL; if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window @@ -547,43 +896,77 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l for (i=0; i < stbiw__ZHASH; ++i) (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); { // compute adler32 on input - unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552; - int j=0; + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; while (j < data_len) { for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; s1 %= 65521, s2 %= 65521; j += blocklen; blocklen = 5552; } - stbiw__sbpush(out, (unsigned char) (s2 >> 8)); - stbiw__sbpush(out, (unsigned char) s2); - stbiw__sbpush(out, (unsigned char) (s1 >> 8)); - stbiw__sbpush(out, (unsigned char) s1); + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); } *out_len = stbiw__sbn(out); // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS } -unsigned int stbiw__crc32(unsigned char *buffer, int len) +static unsigned int stbiw__crc32(unsigned char *buffer, int len) { - static unsigned int crc_table[256]; + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + unsigned int crc = ~0u; - int i,j; - if (crc_table[1] == 0) - for(i=0; i < 256; i++) - for (crc_table[i]=i, j=0; j < 8; ++j) - crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0); + int i; for (i=0; i < len; ++i) crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; return ~crc; } -#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4) +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) @@ -596,66 +979,94 @@ static void stbiw__wpcrc(unsigned char **data, int len) static unsigned char stbiw__paeth(int a, int b, int c) { int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); - if (pa <= pb && pa <= pc) return (unsigned char) a; - if (pb <= pc) return (unsigned char) b; - return (unsigned char) c; + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + for (i = 0; i < n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + for (i=n; i < width*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } } unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { + int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; - int i,j,k,p,zlen; + int j,zlen; if (stride_bytes == 0) stride_bytes = x * n; + if (force_filter >= 5) { + force_filter = -1; + } + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { - static int mapping[] = { 0,1,2,3,4 }; - static int firstmap[] = { 0,1,0,5,6 }; - int *mymap = j ? mapping : firstmap; - int best = 0, bestval = 0x7fffffff; - for (p=0; p < 2; ++p) { - for (k= p?best:0; k < 5; ++k) { - int type = mymap[k],est=0; - unsigned char *z = pixels + stride_bytes*j; - for (i=0; i < n; ++i) - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; - case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; - case 5: line_buffer[i] = z[i]; break; - case 6: line_buffer[i] = z[i]; break; - } - for (i=n; i < x*n; ++i) { - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i] - z[i-n]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; - case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; - case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; - } - } - if (p) break; - for (i=0; i < x*n; ++i) + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); - if (est < bestval) { bestval = est; best = k; } + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; } } - // when we get here, best contains the filter type, and line_buffer contains the data - filt[j*(x*n+1)] = (unsigned char) best; + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); - zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); STBIW_FREE(filt); if (!zlib) return 0; @@ -671,7 +1082,7 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in stbiw__wp32(o, x); stbiw__wp32(o, y); *o++ = 8; - *o++ = (unsigned char) ctype[n]; + *o++ = STBIW_UCHAR(ctype[n]); *o++ = 0; *o++ = 0; *o++ = 0; @@ -693,22 +1104,407 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in return out; } -int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { FILE *f; int len; unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); - if (!png) return 0; + if (png == NULL) return 0; +#ifdef STBI_MSC_SECURE_CRT + if (fopen_s(&f, filename, "wb")) + f = NULL; +#else f = fopen(filename, "wb"); +#endif if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); STBIW_FREE(png); return 1; } +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + const unsigned char *imageData = (const unsigned char *)data; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + int x, y, pos; + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float YDU[64], UDU[64], VDU[64]; + for(row = y, pos = 0; row < y+8; ++row) { + for(col = x; col < x+8; ++col, ++pos) { + int p = (stbi__flip_vertically_on_write ? height-1-row : row)*width*comp + col*comp; + float r, g, b; + if(row >= height) { + p -= width*comp*(row+1 - height); + } + if(col >= width) { + p -= comp*(col+1 - width); + } + + r = imageData[p+0]; + g = imageData[p+ofsG]; + b = imageData[p+ofsB]; + YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; + UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; + VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support 0.98 (2015-04-08) added STBIW_MALLOC, STBIW_ASSERT etc 0.97 (2015-01-18) @@ -728,3 +1524,45 @@ int stbi_write_png(char const *filename, int x, int y, int comp, const void *dat first public release 0.90 first internal release */ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/image.darknet/src/super.c b/image.darknet/src/super.c deleted file mode 100644 index 63e9860..0000000 --- a/image.darknet/src/super.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "network.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -void train_super(char *cfgfile, char *weightfile) -{ - char *train_images = "/data/imagenet/imagenet1k.train.list"; - char *backup_directory = "/home/pjreddie/backup/"; - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - int i = *net.seen/imgs; - data train, buffer; - - - list *plist = get_paths(train_images); - //int N = plist->size; - char **paths = (char **)list_to_array(plist); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.scale = 4; - args.paths = paths; - args.n = imgs; - args.m = plist->size; - args.d = &buffer; - args.type = SUPER_DATA; - - pthread_t load_thread = load_data_in_thread(args); - clock_t time; - //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ - i += 1; - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data_in_thread(args); - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - - time=clock(); - float loss = train_network(net, train); - if (avg_loss < 0) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - - printf("%d: %f, %f avg, %f rate, %lf seconds, %d images\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time), i*imgs); - if(i%1000==0){ - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); - save_weights(net, buff); - } - if(i%100==0){ - char buff[256]; - sprintf(buff, "%s/%s.backup", backup_directory, base); - save_weights(net, buff); - } - free_data(train); - } - char buff[256]; - sprintf(buff, "%s/%s_final.weights", backup_directory, base); - save_weights(net, buff); -} - -void test_super(char *cfgfile, char *weightfile, char *filename) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - - clock_t time; - char buff[256]; - char *input = buff; - 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, "\n"); - } - image im = load_image_color(input, 0, 0); - resize_network(&net, im.w, im.h); - printf("%d %d\n", im.w, im.h); - - float *X = im.data; - time=clock(); - network_predict(net, X); - image out = get_network_image(net); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - save_image(out, "out"); - - free_image(im); - if (filename) break; - } -} - - -void run_super(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *filename = (argc > 5) ? argv[5] : 0; - if(0==strcmp(argv[2], "train")) train_super(cfg, weights); - else if(0==strcmp(argv[2], "test")) test_super(cfg, weights, filename); - /* - else if(0==strcmp(argv[2], "valid")) validate_super(cfg, weights); - */ -} diff --git a/image.darknet/src/swag.c b/image.darknet/src/swag.c deleted file mode 100644 index 2cb3093..0000000 --- a/image.darknet/src/swag.c +++ /dev/null @@ -1,91 +0,0 @@ -#include "network.h" -#include "detection_layer.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "box.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -void train_swag(char *cfgfile, char *weightfile) -{ - char *train_images = "data/voc.0712.trainval"; - char *backup_directory = "/home/pjreddie/backup/"; - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - int i = *net.seen/imgs; - data train, buffer; - - layer l = net.layers[net.n - 1]; - - int side = l.side; - int classes = l.classes; - float jitter = l.jitter; - - list *plist = get_paths(train_images); - //int N = plist->size; - char **paths = (char **)list_to_array(plist); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.paths = paths; - args.n = imgs; - args.m = plist->size; - args.classes = classes; - args.jitter = jitter; - args.num_boxes = side; - args.d = &buffer; - args.type = REGION_DATA; - - pthread_t load_thread = load_data_in_thread(args); - clock_t time; - //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ - i += 1; - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data_in_thread(args); - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - - time=clock(); - float loss = train_network(net, train); - if (avg_loss < 0) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - - printf("%d: %f, %f avg, %f rate, %lf seconds, %d images\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time), i*imgs); - if(i%1000==0 || i == 600){ - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); - save_weights(net, buff); - } - free_data(train); - } - char buff[256]; - sprintf(buff, "%s/%s_final.weights", backup_directory, base); - save_weights(net, buff); -} - -void run_swag(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - if(0==strcmp(argv[2], "train")) train_swag(cfg, weights); -} diff --git a/image.darknet/src/tag.c b/image.darknet/src/tag.c deleted file mode 100644 index 1e43e7d..0000000 --- a/image.darknet/src/tag.c +++ /dev/null @@ -1,153 +0,0 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -void train_tag(char *cfgfile, char *weightfile, int clear) -{ - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - char *backup_directory = "/home/pjreddie/backup/"; - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - if(clear) *net.seen = 0; - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = 1024; - list *plist = get_paths("/home/pjreddie/tag/train.list"); - char **paths = (char **)list_to_array(plist); - printf("%d\n", plist->size); - int N = plist->size; - clock_t time; - pthread_t load_thread; - data train; - data buffer; - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - - args.min = net.w; - args.max = net.max_crop; - args.size = net.w; - - args.paths = paths; - args.classes = net.outputs; - args.n = imgs; - args.m = N; - args.d = &buffer; - args.type = TAG_DATA; - - args.angle = net.angle; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; - - fprintf(stderr, "%d classes\n", net.outputs); - - load_thread = load_data_in_thread(args); - int epoch = (*net.seen)/N; - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - - load_thread = load_data_in_thread(args); - printf("Loaded: %lf seconds\n", sec(clock()-time)); - time=clock(); - float loss = train_network(net, train); - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); - free_data(train); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; - char buff[256]; - sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); - save_weights(net, buff); - } - if(get_current_batch(net)%100 == 0){ - char buff[256]; - sprintf(buff, "%s/%s.backup",backup_directory,base); - save_weights(net, buff); - } - } - char buff[256]; - sprintf(buff, "%s/%s.weights", backup_directory, base); - save_weights(net, buff); - - pthread_join(load_thread, 0); - free_data(buffer); - free_network(net); - free_ptrs((void**)paths, plist->size); - free_list(plist); - free(base); -} - -void test_tag(char *cfgfile, char *weightfile, char *filename) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - int i = 0; - char **names = get_labels("data/tags.txt"); - clock_t time; - int indexes[10]; - char buff[256]; - char *input = buff; - int size = net.w; - 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, "\n"); - } - image im = load_image_color(input, 0, 0); - image r = resize_min(im, size); - resize_network(&net, r.w, r.h); - printf("%d %d\n", r.w, r.h); - - float *X = r.data; - time=clock(); - float *predictions = network_predict(net, X); - top_predictions(net, 10, indexes); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - for(i = 0; i < 10; ++i){ - int index = indexes[i]; - printf("%.1f%%: %s\n", predictions[index]*100, names[index]); - } - if(r.data != im.data) free_image(r); - free_image(im); - if (filename) break; - } -} - - -void run_tag(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - int clear = find_arg(argc, argv, "-clear"); - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *filename = (argc > 5) ? argv[5] : 0; - if(0==strcmp(argv[2], "train")) train_tag(cfg, weights, clear); - else if(0==strcmp(argv[2], "test")) test_tag(cfg, weights, filename); -} - diff --git a/image.darknet/src/tree.c b/image.darknet/src/tree.c index dd44515..67b6d43 100644 --- a/image.darknet/src/tree.c +++ b/image.darknet/src/tree.c @@ -24,33 +24,33 @@ void change_leaves(tree *t, char *leaf_list) fprintf(stderr, "Found %d leaves.\n", found); } -float get_hierarchy_probability(float *x, tree *hier, int c) +float get_hierarchy_probability(float *x, tree *hier, int c, int stride) { float p = 1; while(c >= 0){ - p = p * x[c]; + p = p * x[c*stride]; c = hier->parent[c]; } return p; } -void hierarchy_predictions(float *predictions, int n, tree *hier, int only_leaves) +void hierarchy_predictions(float *predictions, int n, tree *hier, int only_leaves, int stride) { int j; for(j = 0; j < n; ++j){ int parent = hier->parent[j]; if(parent >= 0){ - predictions[j] *= predictions[parent]; + predictions[j*stride] *= predictions[parent*stride]; } } if(only_leaves){ for(j = 0; j < n; ++j){ - if(!hier->leaf[j]) predictions[j] = 0; + if(!hier->leaf[j]) predictions[j*stride] = 0; } } } -int hierarchy_top_prediction(float *predictions, tree *hier, float thresh) +int hierarchy_top_prediction(float *predictions, tree *hier, float thresh, int stride) { float p = 1; int group = 0; @@ -61,7 +61,7 @@ int hierarchy_top_prediction(float *predictions, tree *hier, float thresh) for(i = 0; i < hier->group_size[group]; ++i){ int index = i + hier->group_offset[group]; - float val = predictions[i + hier->group_offset[group]]; + float val = predictions[(i + hier->group_offset[group])*stride]; if(val > max){ max_i = index; max = val; @@ -71,6 +71,8 @@ int hierarchy_top_prediction(float *predictions, tree *hier, float thresh) p = p*max; group = hier->child[max_i]; if(hier->child[max_i] < 0) return max_i; + } else if (group == 0){ + return max_i; } else { return hier->parent[hier->group_offset[group]]; } diff --git a/image.darknet/src/tree.h b/image.darknet/src/tree.h index dbd4c39..3802b8e 100644 --- a/image.darknet/src/tree.h +++ b/image.darknet/src/tree.h @@ -1,23 +1,8 @@ #ifndef TREE_H #define TREE_H +#include "darknet.h" -typedef struct{ - int *leaf; - int n; - int *parent; - int *child; - int *group; - char **name; - - int groups; - int *group_size; - int *group_offset; -} tree; - -tree *read_tree(char *filename); -void hierarchy_predictions(float *predictions, int n, tree *hier, int only_leaves); -void change_leaves(tree *t, char *leaf_list); -int hierarchy_top_prediction(float *predictions, tree *hier, float thresh); -float get_hierarchy_probability(float *x, tree *hier, int c); +int hierarchy_top_prediction(float *predictions, tree *hier, float thresh, int stride); +float get_hierarchy_probability(float *x, tree *hier, int c, int stride); #endif diff --git a/image.darknet/src/upsample_layer.c b/image.darknet/src/upsample_layer.c new file mode 100644 index 0000000..605f21f --- /dev/null +++ b/image.darknet/src/upsample_layer.c @@ -0,0 +1,106 @@ +#include "upsample_layer.h" +#include "cuda.h" +#include "blas.h" + +#include + +layer make_upsample_layer(int batch, int w, int h, int c, int stride) +{ + layer l = {0}; + l.type = UPSAMPLE; + l.batch = batch; + l.w = w; + l.h = h; + l.c = c; + l.out_w = w*stride; + l.out_h = h*stride; + l.out_c = c; + if(stride < 0){ + stride = -stride; + l.reverse=1; + l.out_w = w/stride; + l.out_h = h/stride; + } + l.stride = stride; + l.outputs = l.out_w*l.out_h*l.out_c; + l.inputs = l.w*l.h*l.c; + l.delta = calloc(l.outputs*batch, sizeof(float)); + l.output = calloc(l.outputs*batch, sizeof(float));; + + l.forward = forward_upsample_layer; + l.backward = backward_upsample_layer; + #ifdef GPU + l.forward_gpu = forward_upsample_layer_gpu; + l.backward_gpu = backward_upsample_layer_gpu; + + l.delta_gpu = cuda_make_array(l.delta, l.outputs*batch); + l.output_gpu = cuda_make_array(l.output, l.outputs*batch); + #endif + if(l.reverse) fprintf(stderr, "downsample %2dx %4d x%4d x%4d -> %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c); + else fprintf(stderr, "upsample %2dx %4d x%4d x%4d -> %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c); + return l; +} + +void resize_upsample_layer(layer *l, int w, int h) +{ + l->w = w; + l->h = h; + l->out_w = w*l->stride; + l->out_h = h*l->stride; + if(l->reverse){ + l->out_w = w/l->stride; + l->out_h = h/l->stride; + } + l->outputs = l->out_w*l->out_h*l->out_c; + l->inputs = l->h*l->w*l->c; + l->delta = realloc(l->delta, l->outputs*l->batch*sizeof(float)); + l->output = realloc(l->output, l->outputs*l->batch*sizeof(float)); + +#ifdef GPU + cuda_free(l->output_gpu); + cuda_free(l->delta_gpu); + l->output_gpu = cuda_make_array(l->output, l->outputs*l->batch); + l->delta_gpu = cuda_make_array(l->delta, l->outputs*l->batch); +#endif + +} + +void forward_upsample_layer(const layer l, network net) +{ + fill_cpu(l.outputs*l.batch, 0, l.output, 1); + if(l.reverse){ + upsample_cpu(l.output, l.out_w, l.out_h, l.c, l.batch, l.stride, 0, l.scale, net.input); + }else{ + upsample_cpu(net.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.scale, l.output); + } +} + +void backward_upsample_layer(const layer l, network net) +{ + if(l.reverse){ + upsample_cpu(l.delta, l.out_w, l.out_h, l.c, l.batch, l.stride, 1, l.scale, net.delta); + }else{ + upsample_cpu(net.delta, l.w, l.h, l.c, l.batch, l.stride, 0, l.scale, l.delta); + } +} + +#ifdef GPU +void forward_upsample_layer_gpu(const layer l, network net) +{ + fill_gpu(l.outputs*l.batch, 0, l.output_gpu, 1); + if(l.reverse){ + upsample_gpu(l.output_gpu, l.out_w, l.out_h, l.c, l.batch, l.stride, 0, l.scale, net.input_gpu); + }else{ + upsample_gpu(net.input_gpu, l.w, l.h, l.c, l.batch, l.stride, 1, l.scale, l.output_gpu); + } +} + +void backward_upsample_layer_gpu(const layer l, network net) +{ + if(l.reverse){ + upsample_gpu(l.delta_gpu, l.out_w, l.out_h, l.c, l.batch, l.stride, 1, l.scale, net.delta_gpu); + }else{ + upsample_gpu(net.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 0, l.scale, l.delta_gpu); + } +} +#endif diff --git a/image.darknet/src/upsample_layer.h b/image.darknet/src/upsample_layer.h new file mode 100644 index 0000000..86790d1 --- /dev/null +++ b/image.darknet/src/upsample_layer.h @@ -0,0 +1,15 @@ +#ifndef UPSAMPLE_LAYER_H +#define UPSAMPLE_LAYER_H +#include "darknet.h" + +layer make_upsample_layer(int batch, int w, int h, int c, int stride); +void forward_upsample_layer(const layer l, network net); +void backward_upsample_layer(const layer l, network net); +void resize_upsample_layer(layer *l, int w, int h); + +#ifdef GPU +void forward_upsample_layer_gpu(const layer l, network net); +void backward_upsample_layer_gpu(const layer l, network net); +#endif + +#endif diff --git a/image.darknet/src/utils.c b/image.darknet/src/utils.c index b5181d7..626b467 100644 --- a/image.darknet/src/utils.c +++ b/image.darknet/src/utils.c @@ -6,9 +6,56 @@ #include #include #include +#include +#include #include "utils.h" + +/* +// old timing. is it better? who knows!! +double get_wall_time() +{ + struct timeval time; + if (gettimeofday(&time,NULL)){ + return 0; + } + return (double)time.tv_sec + (double)time.tv_usec * .000001; +} +*/ + +double what_time_is_it_now() +{ + struct timeval time; + if (gettimeofday(&time,NULL)){ + return 0; + } + return (double)time.tv_sec + (double)time.tv_usec * .000001; +} + +int *read_intlist(char *gpu_list, int *ngpus, int d) +{ + int *gpus = 0; + if(gpu_list){ + int len = strlen(gpu_list); + *ngpus = 1; + int i; + for(i = 0; i < len; ++i){ + if (gpu_list[i] == ',') ++*ngpus; + } + gpus = calloc(*ngpus, sizeof(int)); + for(i = 0; i < *ngpus; ++i){ + gpus[i] = atoi(gpu_list); + gpu_list = strchr(gpu_list, ',')+1; + } + } else { + gpus = calloc(1, sizeof(float)); + *gpus = d; + *ngpus = 1; + } + return gpus; +} + int *read_map(char *filename) { int n = 0; @@ -47,6 +94,22 @@ void shuffle(void *arr, size_t n, size_t size) } } +int *random_index_order(int min, int max) +{ + int *inds = calloc(max-min, sizeof(int)); + int i; + for(i = min; i < max; ++i){ + inds[i] = i; + } + for(i = min; i < max-1; ++i){ + int swap = inds[i]; + int index = i + rand()%(max-i); + inds[i] = inds[index]; + inds[index] = swap; + } + return inds; +} + void del_arg(int argc, char **argv, int index) { int i; @@ -194,6 +257,21 @@ void error(const char *s) exit(-1); } +unsigned char *read_file(char *filename) +{ + FILE *fp = fopen(filename, "rb"); + size_t size; + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + unsigned char *text = calloc(size+1, sizeof(char)); + fread(text, 1, size, fp); + fclose(fp); + return text; +} + void malloc_error() { fprintf(stderr, "Malloc error\n"); @@ -524,6 +602,20 @@ int sample_array(float *a, int n) return n-1; } +int max_int_index(int *a, int n) +{ + if(n <= 0) return -1; + int i, max_i = 0; + int max = a[0]; + for(i = 1; i < n; ++i){ + if(a[i] > max){ + max = a[i]; + max_i = i; + } + } + return max_i; +} + int max_index(float *a, int n) { if(n <= 0) return -1; @@ -538,6 +630,15 @@ int max_index(float *a, int n) return max_i; } +int int_index(int *a, int val, int n) +{ + int i; + for(i = 0; i < n; ++i){ + if(a[i] == val) return i; + } + return -1; +} + int rand_int(int min, int max) { if (max < min){ @@ -585,13 +686,13 @@ float rand_normal() size_t rand_size_t() { return ((size_t)(rand()&0xff) << 56) | - ((size_t)(rand()&0xff) << 48) | - ((size_t)(rand()&0xff) << 40) | - ((size_t)(rand()&0xff) << 32) | - ((size_t)(rand()&0xff) << 24) | - ((size_t)(rand()&0xff) << 16) | - ((size_t)(rand()&0xff) << 8) | - ((size_t)(rand()&0xff) << 0); + ((size_t)(rand()&0xff) << 48) | + ((size_t)(rand()&0xff) << 40) | + ((size_t)(rand()&0xff) << 32) | + ((size_t)(rand()&0xff) << 24) | + ((size_t)(rand()&0xff) << 16) | + ((size_t)(rand()&0xff) << 8) | + ((size_t)(rand()&0xff) << 0); } float rand_uniform(float min, float max) diff --git a/image.darknet/src/utils.h b/image.darknet/src/utils.h index bbc6765..ef24da7 100644 --- a/image.darknet/src/utils.h +++ b/image.darknet/src/utils.h @@ -2,16 +2,22 @@ #define UTILS_H #include #include +#include "darknet.h" #include "list.h" -#define SECRET_NUM -1234 -#define TWO_PI 6.2831853071795864769252866 +#define TIME(a) \ + do { \ + double start = what_time_is_it_now(); \ + a; \ + printf("%s took: %f seconds\n", #a, what_time_is_it_now() - start); \ + } while (0) -int *read_map(char *filename); +#define TWO_PI 6.2831853071795864769252866f + +double what_time_is_it_now(); void shuffle(void *arr, size_t n, size_t size); void sorta_shuffle(void *arr, size_t n, size_t size, size_t sections); void free_ptrs(void **ptrs, int n); -char *basecfg(char *cfgfile); int alphanum_to_int(char c); char int_to_alphanum(int i); int read_int(int fd); @@ -21,44 +27,27 @@ void write_all(int fd, char *buffer, size_t bytes); int read_all_fail(int fd, char *buffer, size_t bytes); int write_all_fail(int fd, char *buffer, size_t bytes); void find_replace(char *str, char *orig, char *rep, char *output); -void error(const char *s); void malloc_error(); void file_error(char *s); void strip(char *s); void strip_char(char *s, char bad); -void top_k(float *a, int n, int k, int *index); list *split_str(char *s, char delim); char *fgetl(FILE *fp); list *parse_csv_line(char *line); char *copy_string(char *s); int count_fields(char *line); float *parse_fields(char *line, int n); -void normalize_array(float *a, int n); -void scale_array(float *a, int n, float s); void translate_array(float *a, int n, float s); -int max_index(float *a, int n); float constrain(float min, float max, float a); int constrain_int(int a, int min, int max); -float mse_array(float *a, int n); -float rand_normal(); -size_t rand_size_t(); -float rand_uniform(float min, float max); float rand_scale(float s); int rand_int(int min, int max); -float sum_array(float *a, int n); -float mean_array(float *a, int n); void mean_arrays(float **a, int n, int els, float *avg); -float variance_array(float *a, int n); -float mag_array(float *a, int n); float dist_array(float *a, float *b, int n, int sub); float **one_hot_encode(float *a, int n, int k); float sec(clock_t clocks); -int find_int_arg(int argc, char **argv, char *arg, int def); -float find_float_arg(int argc, char **argv, char *arg, float def); -int find_arg(int argc, char* argv[], char *arg); -char *find_char_arg(int argc, char **argv, char *arg, char *def); -int sample_array(float *a, int n); void print_statistics(float *a, int n); +int int_index(int *a, int val, int n); #endif diff --git a/image.darknet/src/voxel.c b/image.darknet/src/voxel.c deleted file mode 100644 index 1b53880..0000000 --- a/image.darknet/src/voxel.c +++ /dev/null @@ -1,169 +0,0 @@ -#include "network.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -image get_image_from_stream(CvCapture *cap); -#endif - -void extract_voxel(char *lfile, char *rfile, char *prefix) -{ -#ifdef OPENCV - int w = 1920; - int h = 1080; - int shift = 0; - int count = 0; - CvCapture *lcap = cvCaptureFromFile(lfile); - CvCapture *rcap = cvCaptureFromFile(rfile); - while(1){ - image l = get_image_from_stream(lcap); - image r = get_image_from_stream(rcap); - if(!l.w || !r.w) break; - if(count%100 == 0) { - shift = best_3d_shift_r(l, r, -l.h/100, l.h/100); - printf("%d\n", shift); - } - image ls = crop_image(l, (l.w - w)/2, (l.h - h)/2, w, h); - image rs = crop_image(r, 105 + (r.w - w)/2, (r.h - h)/2 + shift, w, h); - char buff[256]; - sprintf(buff, "%s_%05d_l", prefix, count); - save_image(ls, buff); - sprintf(buff, "%s_%05d_r", prefix, count); - save_image(rs, buff); - free_image(l); - free_image(r); - free_image(ls); - free_image(rs); - ++count; - } - -#else - printf("need OpenCV for extraction\n"); -#endif -} - -void train_voxel(char *cfgfile, char *weightfile) -{ - char *train_images = "/data/imagenet/imagenet1k.train.list"; - char *backup_directory = "/home/pjreddie/backup/"; - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - int i = *net.seen/imgs; - data train, buffer; - - - list *plist = get_paths(train_images); - //int N = plist->size; - char **paths = (char **)list_to_array(plist); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.scale = 4; - args.paths = paths; - args.n = imgs; - args.m = plist->size; - args.d = &buffer; - args.type = SUPER_DATA; - - pthread_t load_thread = load_data_in_thread(args); - clock_t time; - //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ - i += 1; - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data_in_thread(args); - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - - time=clock(); - float loss = train_network(net, train); - if (avg_loss < 0) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - - printf("%d: %f, %f avg, %f rate, %lf seconds, %d images\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time), i*imgs); - if(i%1000==0){ - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); - save_weights(net, buff); - } - if(i%100==0){ - char buff[256]; - sprintf(buff, "%s/%s.backup", backup_directory, base); - save_weights(net, buff); - } - free_data(train); - } - char buff[256]; - sprintf(buff, "%s/%s_final.weights", backup_directory, base); - save_weights(net, buff); -} - -void test_voxel(char *cfgfile, char *weightfile, char *filename) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - - clock_t time; - char buff[256]; - char *input = buff; - 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, "\n"); - } - image im = load_image_color(input, 0, 0); - resize_network(&net, im.w, im.h); - printf("%d %d\n", im.w, im.h); - - float *X = im.data; - time=clock(); - network_predict(net, X); - image out = get_network_image(net); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - save_image(out, "out"); - - free_image(im); - if (filename) break; - } -} - - -void run_voxel(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *filename = (argc > 5) ? argv[5] : 0; - if(0==strcmp(argv[2], "train")) train_voxel(cfg, weights); - else if(0==strcmp(argv[2], "test")) test_voxel(cfg, weights, filename); - else if(0==strcmp(argv[2], "extract")) extract_voxel(argv[3], argv[4], argv[5]); - /* - else if(0==strcmp(argv[2], "valid")) validate_voxel(cfg, weights); - */ -} diff --git a/image.darknet/src/writing.c b/image.darknet/src/writing.c deleted file mode 100644 index 0a76d48..0000000 --- a/image.darknet/src/writing.c +++ /dev/null @@ -1,150 +0,0 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -void train_writing(char *cfgfile, char *weightfile) -{ - char *backup_directory = "/home/pjreddie/backup/"; - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - list *plist = get_paths("figures.list"); - char **paths = (char **)list_to_array(plist); - clock_t time; - int N = plist->size; - printf("N: %d\n", N); - image out = get_network_image(net); - - data train, buffer; - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.out_w = out.w; - args.out_h = out.h; - args.paths = paths; - args.n = imgs; - args.m = N; - args.d = &buffer; - args.type = WRITING_DATA; - - pthread_t load_thread = load_data_in_thread(args); - int epoch = (*net.seen)/N; - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data_in_thread(args); - printf("Loaded %lf seconds\n",sec(clock()-time)); - - time=clock(); - float loss = train_network(net, train); - - /* - image pred = float_to_image(64, 64, 1, out); - print_image(pred); - */ - - /* - image im = float_to_image(256, 256, 3, train.X.vals[0]); - image lab = float_to_image(64, 64, 1, train.y.vals[0]); - image pred = float_to_image(64, 64, 1, out); - show_image(im, "image"); - show_image(lab, "label"); - print_image(lab); - show_image(pred, "pred"); - cvWaitKey(0); - */ - - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); - free_data(train); - if(get_current_batch(net)%100 == 0){ - char buff[256]; - sprintf(buff, "%s/%s_batch_%d.weights", backup_directory, base, get_current_batch(net)); - save_weights(net, buff); - } - if(*net.seen/N > epoch){ - epoch = *net.seen/N; - char buff[256]; - sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); - save_weights(net, buff); - } - } -} - -void test_writing(char *cfgfile, char *weightfile, char *filename) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - clock_t time; - char buff[256]; - char *input = buff; - 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, "\n"); - } - - image im = load_image_color(input, 0, 0); - resize_network(&net, im.w, im.h); - printf("%d %d %d\n", im.h, im.w, im.c); - float *X = im.data; - time=clock(); - network_predict(net, X); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - image pred = get_network_image(net); - - image upsampled = resize_image(pred, im.w, im.h); - image thresh = threshold_image(upsampled, .5); - pred = thresh; - - show_image(pred, "prediction"); - show_image(im, "orig"); -#ifdef OPENCV - cvWaitKey(0); - cvDestroyAllWindows(); -#endif - - free_image(upsampled); - free_image(thresh); - free_image(im); - if (filename) break; - } -} - -void run_writing(int argc, char **argv) -{ - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *filename = (argc > 5) ? argv[5] : 0; - if(0==strcmp(argv[2], "train")) train_writing(cfg, weights); - else if(0==strcmp(argv[2], "test")) test_writing(cfg, weights, filename); -} - diff --git a/image.darknet/src/yolo.c b/image.darknet/src/yolo.c deleted file mode 100644 index ee5f73b..0000000 --- a/image.darknet/src/yolo.c +++ /dev/null @@ -1,355 +0,0 @@ -#include "network.h" -#include "detection_layer.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "box.h" -#include "demo.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -char *voc_names[] = {"aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"}; - -void train_yolo(char *cfgfile, char *weightfile) -{ - char *train_images = "/data/voc/train.txt"; - char *backup_directory = "/home/pjreddie/backup/"; - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - int i = *net.seen/imgs; - data train, buffer; - - - layer l = net.layers[net.n - 1]; - - int side = l.side; - int classes = l.classes; - float jitter = l.jitter; - - list *plist = get_paths(train_images); - //int N = plist->size; - char **paths = (char **)list_to_array(plist); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.paths = paths; - args.n = imgs; - args.m = plist->size; - args.classes = classes; - args.jitter = jitter; - args.num_boxes = side; - args.d = &buffer; - args.type = REGION_DATA; - - args.angle = net.angle; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; - - pthread_t load_thread = load_data_in_thread(args); - clock_t time; - //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ - i += 1; - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data_in_thread(args); - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - - time=clock(); - float loss = train_network(net, train); - if (avg_loss < 0) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - - printf("%d: %f, %f avg, %f rate, %lf seconds, %d images\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time), i*imgs); - if(i%1000==0 || (i < 1000 && i%100 == 0)){ - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); - save_weights(net, buff); - } - free_data(train); - } - char buff[256]; - sprintf(buff, "%s/%s_final.weights", backup_directory, base); - save_weights(net, buff); -} - -void print_yolo_detections(FILE **fps, char *id, box *boxes, float **probs, int total, int classes, int w, int h) -{ - int i, j; - for(i = 0; i < total; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; - - if (xmin < 0) xmin = 0; - if (ymin < 0) ymin = 0; - if (xmax > w) xmax = w; - if (ymax > h) ymax = h; - - for(j = 0; j < classes; ++j){ - if (probs[i][j]) fprintf(fps[j], "%s %f %f %f %f %f\n", id, probs[i][j], - xmin, ymin, xmax, ymax); - } - } -} - -void validate_yolo(char *cfgfile, char *weightfile) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - srand(time(0)); - - char *base = "results/comp4_det_test_"; - //list *plist = get_paths("data/voc.2007.test"); - list *plist = get_paths("/home/pjreddie/data/voc/2007_test.txt"); - //list *plist = get_paths("data/voc.2012.test"); - char **paths = (char **)list_to_array(plist); - - layer l = net.layers[net.n-1]; - int classes = l.classes; - - int j; - FILE **fps = calloc(classes, sizeof(FILE *)); - for(j = 0; j < classes; ++j){ - char buff[1024]; - snprintf(buff, 1024, "%s%s.txt", base, voc_names[j]); - fps[j] = fopen(buff, "w"); - } - box *boxes = calloc(l.side*l.side*l.n, sizeof(box)); - float **probs = calloc(l.side*l.side*l.n, sizeof(float *)); - for(j = 0; j < l.side*l.side*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); - - int m = plist->size; - int i=0; - int t; - - float thresh = .001; - int nms = 1; - float iou_thresh = .5; - - int nthreads = 8; - image *val = calloc(nthreads, sizeof(image)); - image *val_resized = calloc(nthreads, sizeof(image)); - image *buf = calloc(nthreads, sizeof(image)); - image *buf_resized = calloc(nthreads, sizeof(image)); - pthread_t *thr = calloc(nthreads, sizeof(pthread_t)); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.type = IMAGE_DATA; - - for(t = 0; t < nthreads; ++t){ - args.path = paths[i+t]; - args.im = &buf[t]; - args.resized = &buf_resized[t]; - thr[t] = load_data_in_thread(args); - } - time_t start = time(0); - for(i = nthreads; i < m+nthreads; i += nthreads){ - fprintf(stderr, "%d\n", i); - for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ - pthread_join(thr[t], 0); - val[t] = buf[t]; - val_resized[t] = buf_resized[t]; - } - for(t = 0; t < nthreads && i+t < m; ++t){ - args.path = paths[i+t]; - args.im = &buf[t]; - args.resized = &buf_resized[t]; - thr[t] = load_data_in_thread(args); - } - for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ - char *path = paths[i+t-nthreads]; - char *id = basecfg(path); - float *X = val_resized[t].data; - network_predict(net, X); - int w = val[t].w; - int h = val[t].h; - get_detection_boxes(l, w, h, thresh, probs, boxes, 0); - if (nms) do_nms_sort(boxes, probs, l.side*l.side*l.n, classes, iou_thresh); - print_yolo_detections(fps, id, boxes, probs, l.side*l.side*l.n, classes, w, h); - free(id); - free_image(val[t]); - free_image(val_resized[t]); - } - } - fprintf(stderr, "Total Detection Time: %f Seconds\n", (double)(time(0) - start)); -} - -void validate_yolo_recall(char *cfgfile, char *weightfile) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - srand(time(0)); - - char *base = "results/comp4_det_test_"; - list *plist = get_paths("data/voc.2007.test"); - char **paths = (char **)list_to_array(plist); - - layer l = net.layers[net.n-1]; - int classes = l.classes; - int side = l.side; - - int j, k; - FILE **fps = calloc(classes, sizeof(FILE *)); - for(j = 0; j < classes; ++j){ - char buff[1024]; - snprintf(buff, 1024, "%s%s.txt", base, voc_names[j]); - fps[j] = fopen(buff, "w"); - } - box *boxes = calloc(side*side*l.n, sizeof(box)); - float **probs = calloc(side*side*l.n, sizeof(float *)); - for(j = 0; j < side*side*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); - - int m = plist->size; - int i=0; - - float thresh = .001; - float iou_thresh = .5; - float nms = 0; - - int total = 0; - int correct = 0; - int proposals = 0; - float avg_iou = 0; - - for(i = 0; i < m; ++i){ - char *path = paths[i]; - image orig = load_image_color(path, 0, 0); - image sized = resize_image(orig, net.w, net.h); - char *id = basecfg(path); - network_predict(net, sized.data); - get_detection_boxes(l, orig.w, orig.h, thresh, probs, boxes, 1); - if (nms) do_nms(boxes, probs, side*side*l.n, 1, nms); - - char labelpath[4096]; - find_replace(path, "images", "labels", labelpath); - find_replace(labelpath, "JPEGImages", "labels", labelpath); - find_replace(labelpath, ".jpg", ".txt", labelpath); - find_replace(labelpath, ".JPEG", ".txt", labelpath); - - int num_labels = 0; - box_label *truth = read_boxes(labelpath, &num_labels); - for(k = 0; k < side*side*l.n; ++k){ - if(probs[k][0] > thresh){ - ++proposals; - } - } - for (j = 0; j < num_labels; ++j) { - ++total; - box t = {truth[j].x, truth[j].y, truth[j].w, truth[j].h}; - float best_iou = 0; - for(k = 0; k < side*side*l.n; ++k){ - float iou = box_iou(boxes[k], t); - if(probs[k][0] > thresh && iou > best_iou){ - best_iou = iou; - } - } - avg_iou += best_iou; - if(best_iou > iou_thresh){ - ++correct; - } - } - - fprintf(stderr, "%5d %5d %5d\tRPs/Img: %.2f\tIOU: %.2f%%\tRecall:%.2f%%\n", i, correct, total, (float)proposals/(i+1), avg_iou*100/total, 100.*correct/total); - free(id); - free_image(orig); - free_image(sized); - } -} - -void test_yolo(char *cfgfile, char *weightfile, char *filename, float thresh) -{ - image **alphabet = load_alphabet(); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - detection_layer l = net.layers[net.n-1]; - set_batch_network(&net, 1); - srand(2222222); - clock_t time; - char buff[256]; - char *input = buff; - int j; - float nms=.4; - box *boxes = calloc(l.side*l.side*l.n, sizeof(box)); - float **probs = calloc(l.side*l.side*l.n, sizeof(float *)); - for(j = 0; j < l.side*l.side*l.n; ++j) probs[j] = calloc(l.classes, sizeof(float *)); - 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, "\n"); - } - image im = load_image_color(input,0,0); - image sized = resize_image(im, net.w, net.h); - float *X = sized.data; - time=clock(); - network_predict(net, X); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - get_detection_boxes(l, 1, 1, thresh, probs, boxes, 0); - if (nms) do_nms_sort(boxes, probs, l.side*l.side*l.n, l.classes, nms); - //draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, voc_names, alphabet, 20); - draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, voc_names, alphabet, 20); - save_image(im, "predictions"); - show_image(im, "predictions"); - - free_image(im); - free_image(sized); -#ifdef OPENCV - cvWaitKey(0); - cvDestroyAllWindows(); -#endif - if (filename) break; - } -} - -void run_yolo(int argc, char **argv) -{ - char *prefix = find_char_arg(argc, argv, "-prefix", 0); - float thresh = find_float_arg(argc, argv, "-thresh", .2); - int cam_index = find_int_arg(argc, argv, "-c", 0); - int frame_skip = find_int_arg(argc, argv, "-s", 0); - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *filename = (argc > 5) ? argv[5]: 0; - if(0==strcmp(argv[2], "test")) test_yolo(cfg, weights, filename, thresh); - else if(0==strcmp(argv[2], "train")) train_yolo(cfg, weights); - else if(0==strcmp(argv[2], "valid")) validate_yolo(cfg, weights); - else if(0==strcmp(argv[2], "recall")) validate_yolo_recall(cfg, weights); - else if(0==strcmp(argv[2], "demo")) demo(cfg, weights, thresh, cam_index, filename, voc_names, 20, frame_skip, prefix, .5); -} diff --git a/image.darknet/src/yolo_layer.c b/image.darknet/src/yolo_layer.c new file mode 100644 index 0000000..c338036 --- /dev/null +++ b/image.darknet/src/yolo_layer.c @@ -0,0 +1,374 @@ +#include "yolo_layer.h" +#include "activations.h" +#include "blas.h" +#include "box.h" +#include "cuda.h" +#include "utils.h" + +#include +#include +#include +#include + +layer make_yolo_layer(int batch, int w, int h, int n, int total, int *mask, int classes) +{ + int i; + layer l = {0}; + l.type = YOLO; + + l.n = n; + l.total = total; + l.batch = batch; + l.h = h; + l.w = w; + l.c = n*(classes + 4 + 1); + l.out_w = l.w; + l.out_h = l.h; + l.out_c = l.c; + l.classes = classes; + l.cost = calloc(1, sizeof(float)); + l.biases = calloc(total*2, sizeof(float)); + if(mask) l.mask = mask; + else{ + l.mask = calloc(n, sizeof(int)); + for(i = 0; i < n; ++i){ + l.mask[i] = i; + } + } + l.bias_updates = calloc(n*2, sizeof(float)); + l.outputs = h*w*n*(classes + 4 + 1); + l.inputs = l.outputs; + l.truths = 90*(4 + 1); + l.delta = calloc(batch*l.outputs, sizeof(float)); + l.output = calloc(batch*l.outputs, sizeof(float)); + for(i = 0; i < total*2; ++i){ + l.biases[i] = .5; + } + + l.forward = forward_yolo_layer; + l.backward = backward_yolo_layer; +#ifdef GPU + l.forward_gpu = forward_yolo_layer_gpu; + l.backward_gpu = backward_yolo_layer_gpu; + l.output_gpu = cuda_make_array(l.output, batch*l.outputs); + l.delta_gpu = cuda_make_array(l.delta, batch*l.outputs); +#endif + + fprintf(stderr, "yolo\n"); + srand(0); + + return l; +} + +void resize_yolo_layer(layer *l, int w, int h) +{ + l->w = w; + l->h = h; + + l->outputs = h*w*l->n*(l->classes + 4 + 1); + l->inputs = l->outputs; + + l->output = realloc(l->output, l->batch*l->outputs*sizeof(float)); + l->delta = realloc(l->delta, l->batch*l->outputs*sizeof(float)); + +#ifdef GPU + cuda_free(l->delta_gpu); + cuda_free(l->output_gpu); + + l->delta_gpu = cuda_make_array(l->delta, l->batch*l->outputs); + l->output_gpu = cuda_make_array(l->output, l->batch*l->outputs); +#endif +} + +box get_yolo_box(float *x, float *biases, int n, int index, int i, int j, int lw, int lh, int w, int h, int stride) +{ + box b; + b.x = (i + x[index + 0*stride]) / lw; + b.y = (j + x[index + 1*stride]) / lh; + b.w = exp(x[index + 2*stride]) * biases[2*n] / w; + b.h = exp(x[index + 3*stride]) * biases[2*n+1] / h; + return b; +} + +float delta_yolo_box(box truth, float *x, float *biases, int n, int index, int i, int j, int lw, int lh, int w, int h, float *delta, float scale, int stride) +{ + box pred = get_yolo_box(x, biases, n, index, i, j, lw, lh, w, h, stride); + float iou = box_iou(pred, truth); + + float tx = (truth.x*lw - i); + float ty = (truth.y*lh - j); + float tw = log(truth.w*w / biases[2*n]); + float th = log(truth.h*h / biases[2*n + 1]); + + delta[index + 0*stride] = scale * (tx - x[index + 0*stride]); + delta[index + 1*stride] = scale * (ty - x[index + 1*stride]); + delta[index + 2*stride] = scale * (tw - x[index + 2*stride]); + delta[index + 3*stride] = scale * (th - x[index + 3*stride]); + return iou; +} + + +void delta_yolo_class(float *output, float *delta, int index, int class, int classes, int stride, float *avg_cat) +{ + int n; + if (delta[index]){ + delta[index + stride*class] = 1 - output[index + stride*class]; + if(avg_cat) *avg_cat += output[index + stride*class]; + return; + } + for(n = 0; n < classes; ++n){ + delta[index + stride*n] = ((n == class)?1 : 0) - output[index + stride*n]; + if(n == class && avg_cat) *avg_cat += output[index + stride*n]; + } +} + +static int entry_index(layer l, int batch, int location, int entry) +{ + int n = location / (l.w*l.h); + int loc = location % (l.w*l.h); + return batch*l.outputs + n*l.w*l.h*(4+l.classes+1) + entry*l.w*l.h + loc; +} + +void forward_yolo_layer(const layer l, network net) +{ + int i,j,b,t,n; + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); + +#ifndef GPU + for (b = 0; b < l.batch; ++b){ + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + activate_array(l.output + index, 2*l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, 4); + activate_array(l.output + index, (1+l.classes)*l.w*l.h, LOGISTIC); + } + } +#endif + + memset(l.delta, 0, l.outputs * l.batch * sizeof(float)); + if(!net.train) return; + float avg_iou = 0; + float recall = 0; + float recall75 = 0; + float avg_cat = 0; + float avg_obj = 0; + float avg_anyobj = 0; + int count = 0; + int class_count = 0; + *(l.cost) = 0; + for (b = 0; b < l.batch; ++b) { + for (j = 0; j < l.h; ++j) { + for (i = 0; i < l.w; ++i) { + for (n = 0; n < l.n; ++n) { + int box_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 0); + box pred = get_yolo_box(l.output, l.biases, l.mask[n], box_index, i, j, l.w, l.h, net.w, net.h, l.w*l.h); + float best_iou = 0; + int best_t = 0; + for(t = 0; t < l.max_boxes; ++t){ + box truth = float_to_box(net.truth + t*(4 + 1) + b*l.truths, 1); + if(!truth.x) break; + float iou = box_iou(pred, truth); + if (iou > best_iou) { + best_iou = iou; + best_t = t; + } + } + int obj_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 4); + avg_anyobj += l.output[obj_index]; + l.delta[obj_index] = 0 - l.output[obj_index]; + if (best_iou > l.ignore_thresh) { + l.delta[obj_index] = 0; + } + if (best_iou > l.truth_thresh) { + l.delta[obj_index] = 1 - l.output[obj_index]; + + int class = net.truth[best_t*(4 + 1) + b*l.truths + 4]; + if (l.map) class = l.map[class]; + int class_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 4 + 1); + delta_yolo_class(l.output, l.delta, class_index, class, l.classes, l.w*l.h, 0); + box truth = float_to_box(net.truth + best_t*(4 + 1) + b*l.truths, 1); + delta_yolo_box(truth, l.output, l.biases, l.mask[n], box_index, i, j, l.w, l.h, net.w, net.h, l.delta, (2-truth.w*truth.h), l.w*l.h); + } + } + } + } + for(t = 0; t < l.max_boxes; ++t){ + box truth = float_to_box(net.truth + t*(4 + 1) + b*l.truths, 1); + + if(!truth.x) break; + float best_iou = 0; + int best_n = 0; + i = (truth.x * l.w); + j = (truth.y * l.h); + box truth_shift = truth; + truth_shift.x = truth_shift.y = 0; + for(n = 0; n < l.total; ++n){ + box pred = {0}; + pred.w = l.biases[2*n]/net.w; + pred.h = l.biases[2*n+1]/net.h; + float iou = box_iou(pred, truth_shift); + if (iou > best_iou){ + best_iou = iou; + best_n = n; + } + } + + int mask_n = int_index(l.mask, best_n, l.n); + if(mask_n >= 0){ + int box_index = entry_index(l, b, mask_n*l.w*l.h + j*l.w + i, 0); + float iou = delta_yolo_box(truth, l.output, l.biases, best_n, box_index, i, j, l.w, l.h, net.w, net.h, l.delta, (2-truth.w*truth.h), l.w*l.h); + + int obj_index = entry_index(l, b, mask_n*l.w*l.h + j*l.w + i, 4); + avg_obj += l.output[obj_index]; + l.delta[obj_index] = 1 - l.output[obj_index]; + + int class = net.truth[t*(4 + 1) + b*l.truths + 4]; + if (l.map) class = l.map[class]; + int class_index = entry_index(l, b, mask_n*l.w*l.h + j*l.w + i, 4 + 1); + delta_yolo_class(l.output, l.delta, class_index, class, l.classes, l.w*l.h, &avg_cat); + + ++count; + ++class_count; + if(iou > .5) recall += 1; + if(iou > .75) recall75 += 1; + avg_iou += iou; + } + } + } + *(l.cost) = pow(mag_array(l.delta, l.outputs * l.batch), 2); + printf("Region %d Avg IOU: %f, Class: %f, Obj: %f, No Obj: %f, .5R: %f, .75R: %f, count: %d\n", net.index, avg_iou/count, avg_cat/class_count, avg_obj/count, avg_anyobj/(l.w*l.h*l.n*l.batch), recall/count, recall75/count, count); +} + +void backward_yolo_layer(const layer l, network net) +{ + axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, net.delta, 1); +} + +void correct_yolo_boxes(detection *dets, int n, int w, int h, int netw, int neth, int relative) +{ + int i; + int new_w=0; + int new_h=0; + if (((float)netw/w) < ((float)neth/h)) { + new_w = netw; + new_h = (h * netw)/w; + } else { + new_h = neth; + new_w = (w * neth)/h; + } + for (i = 0; i < n; ++i){ + box b = dets[i].bbox; + b.x = (b.x - (netw - new_w)/2./netw) / ((float)new_w/netw); + b.y = (b.y - (neth - new_h)/2./neth) / ((float)new_h/neth); + b.w *= (float)netw/new_w; + b.h *= (float)neth/new_h; + if(!relative){ + b.x *= w; + b.w *= w; + b.y *= h; + b.h *= h; + } + dets[i].bbox = b; + } +} + +int yolo_num_detections(layer l, float thresh) +{ + int i, n; + int count = 0; + for (i = 0; i < l.w*l.h; ++i){ + for(n = 0; n < l.n; ++n){ + int obj_index = entry_index(l, 0, n*l.w*l.h + i, 4); + if(l.output[obj_index] > thresh){ + ++count; + } + } + } + return count; +} + +void avg_flipped_yolo(layer l) +{ + int i,j,n,z; + float *flip = l.output + l.outputs; + for (j = 0; j < l.h; ++j) { + for (i = 0; i < l.w/2; ++i) { + for (n = 0; n < l.n; ++n) { + for(z = 0; z < l.classes + 4 + 1; ++z){ + int i1 = z*l.w*l.h*l.n + n*l.w*l.h + j*l.w + i; + int i2 = z*l.w*l.h*l.n + n*l.w*l.h + j*l.w + (l.w - i - 1); + float swap = flip[i1]; + flip[i1] = flip[i2]; + flip[i2] = swap; + if(z == 0){ + flip[i1] = -flip[i1]; + flip[i2] = -flip[i2]; + } + } + } + } + } + for(i = 0; i < l.outputs; ++i){ + l.output[i] = (l.output[i] + flip[i])/2.; + } +} + +int get_yolo_detections(layer l, int w, int h, int netw, int neth, float thresh, int *map, int relative, detection *dets) +{ + int i,j,n; + float *predictions = l.output; + if (l.batch == 2) avg_flipped_yolo(l); + int count = 0; + for (i = 0; i < l.w*l.h; ++i){ + int row = i / l.w; + int col = i % l.w; + for(n = 0; n < l.n; ++n){ + int obj_index = entry_index(l, 0, n*l.w*l.h + i, 4); + float objectness = predictions[obj_index]; + if(objectness <= thresh) continue; + int box_index = entry_index(l, 0, n*l.w*l.h + i, 0); + dets[count].bbox = get_yolo_box(predictions, l.biases, l.mask[n], box_index, col, row, l.w, l.h, netw, neth, l.w*l.h); + dets[count].objectness = objectness; + dets[count].classes = l.classes; + for(j = 0; j < l.classes; ++j){ + int class_index = entry_index(l, 0, n*l.w*l.h + i, 4 + 1 + j); + float prob = objectness*predictions[class_index]; + dets[count].prob[j] = (prob > thresh) ? prob : 0; + } + ++count; + } + } + correct_yolo_boxes(dets, count, w, h, netw, neth, relative); + return count; +} + +#ifdef GPU + +void forward_yolo_layer_gpu(const layer l, network net) +{ + copy_gpu(l.batch*l.inputs, net.input_gpu, 1, l.output_gpu, 1); + int b, n; + for (b = 0; b < l.batch; ++b){ + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + activate_array_gpu(l.output_gpu + index, 2*l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, 4); + activate_array_gpu(l.output_gpu + index, (1+l.classes)*l.w*l.h, LOGISTIC); + } + } + if(!net.train || l.onlyforward){ + cuda_pull_array(l.output_gpu, l.output, l.batch*l.outputs); + return; + } + + cuda_pull_array(l.output_gpu, net.input, l.batch*l.inputs); + forward_yolo_layer(l, net); + cuda_push_array(l.delta_gpu, l.delta, l.batch*l.outputs); +} + +void backward_yolo_layer_gpu(const layer l, network net) +{ + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); +} +#endif + diff --git a/image.darknet/src/yolo_layer.h b/image.darknet/src/yolo_layer.h new file mode 100644 index 0000000..d2a0243 --- /dev/null +++ b/image.darknet/src/yolo_layer.h @@ -0,0 +1,19 @@ +#ifndef YOLO_LAYER_H +#define YOLO_LAYER_H + +#include "darknet.h" +#include "layer.h" +#include "network.h" + +layer make_yolo_layer(int batch, int w, int h, int n, int total, int *mask, int classes); +void forward_yolo_layer(const layer l, network net); +void backward_yolo_layer(const layer l, network net); +void resize_yolo_layer(layer *l, int w, int h); +int yolo_num_detections(layer l, float thresh); + +#ifdef GPU +void forward_yolo_layer_gpu(const layer l, network net); +void backward_yolo_layer_gpu(layer l, network net); +#endif + +#endif From 372151cc0e101906896bb2b10fd81417e2e7434a Mon Sep 17 00:00:00 2001 From: Jeroen Staab Date: Mon, 24 Dec 2018 16:15:20 +0100 Subject: [PATCH 3/8] Updated inst/include/darknet/ with YOLO repository. An dynamic link into latest version of yolo would be cool.. --- .../inst/include/darknet/LICENSE.fuck | 13 + .../inst/include/darknet/LICENSE.gen | 91 + .../inst/include/darknet/LICENSE.gpl | 674 ++++ .../inst/include/darknet/LICENSE.meta | 8 + .../inst/include/darknet/LICENSE.mit | 22 + image.darknet/inst/include/darknet/LICENSE.v1 | 13 + image.darknet/inst/include/darknet/Makefile | 51 +- .../inst/include/darknet/Makefile.bak | 82 + image.darknet/inst/include/darknet/README.md | 2 +- .../inst/include/darknet/cfg/alexnet.cfg | 9 +- .../inst/include/darknet/cfg/cifar.cfg | 27 +- .../inst/include/darknet/cfg/cifar.test.cfg | 2 - .../inst/include/darknet/cfg/coco.data | 4 +- .../inst/include/darknet/cfg/darknet.cfg | 33 +- .../inst/include/darknet/cfg/darknet19.cfg | 29 +- .../include/darknet/cfg/darknet19_448.cfg | 3 - .../inst/include/darknet/cfg/darknet53.cfg | 566 ++++ .../cfg/{msr_50.cfg => darknet53_448.cfg} | 371 +-- .../cfg/{yolo-voc.cfg => darknet9000.cfg} | 95 +- .../inst/include/darknet/cfg/densenet201.cfg | 1951 ++++++++++++ .../inst/include/darknet/cfg/extraction.cfg | 11 +- .../include/darknet/cfg/extraction22k.cfg | 3 - image.darknet/inst/include/darknet/cfg/go.cfg | 132 + .../inst/include/darknet/cfg/go.test.cfg | 37 +- .../inst/include/darknet/cfg/gru.cfg | 25 +- .../include/darknet/cfg/imagenet22k.dataset | 1 + .../darknet/cfg/imagenet9k.hierarchy.dataset | 9 + .../inst/include/darknet/cfg/openimages.data | 8 + .../inst/include/darknet/cfg/resnet101.cfg | 990 ++++++ .../cfg/{msr_152.cfg => resnet152.cfg} | 302 +- .../cfg/{yolov1/yolo2.cfg => resnet18.cfg} | 201 +- .../darknet/cfg/{msr_34.cfg => resnet34.cfg} | 158 +- .../inst/include/darknet/cfg/resnet50.cfg | 510 +++ .../include/darknet/cfg/resnext101-32x4d.cfg | 1053 +++++++ .../include/darknet/cfg/resnext152-32x4d.cfg | 1562 ++++++++++ .../inst/include/darknet/cfg/resnext50.cfg | 523 ++++ .../inst/include/darknet/cfg/rnn.cfg | 2 - .../inst/include/darknet/cfg/rnn.train.cfg | 2 - .../inst/include/darknet/cfg/strided.cfg | 3 - .../inst/include/darknet/cfg/tiny.cfg | 6 +- .../inst/include/darknet/cfg/vgg-16.cfg | 12 +- .../inst/include/darknet/cfg/yolo9000.cfg | 19 +- .../{yolov1/tiny-yolo.cfg => yolov1-tiny.cfg} | 8 +- .../cfg/{yolov1/yolo.cfg => yolov1.cfg} | 4 + .../include/darknet/cfg/yolov1/tiny-coco.cfg | 125 - .../include/darknet/cfg/yolov1/xyolo.test.cfg | 143 - .../include/darknet/cfg/yolov1/yolo-coco.cfg | 255 -- .../include/darknet/cfg/yolov1/yolo-small.cfg | 239 -- .../include/darknet/cfg/yolov1/yolo.train.cfg | 257 -- ...{tiny-yolo-voc.cfg => yolov2-tiny-voc.cfg} | 10 +- .../cfg/{tiny-yolo.cfg => yolov2-tiny.cfg} | 21 +- .../darknet/cfg/{yolo.cfg => yolov2-voc.cfg} | 34 +- .../include/darknet/cfg/yolov3-openimages.cfg | 789 +++++ .../inst/include/darknet/cfg/yolov3-spp.cfg | 822 +++++ .../inst/include/darknet/cfg/yolov3-tiny.cfg | 182 ++ .../inst/include/darknet/cfg/yolov3-voc.cfg | 785 +++++ .../inst/include/darknet/data/kite.jpg | Bin 0 -> 1415684 bytes .../include/darknet/data/openimages.names | 601 ++++ .../include/darknet/{src => examples}/art.c | 32 +- .../inst/include/darknet/examples/attention.c | 459 +++ .../darknet/{src => examples}/captcha.c | 55 +- .../include/darknet/{src => examples}/cifar.c | 72 +- .../darknet/{src => examples}/classifier.c | 555 ++-- .../include/darknet/{src => examples}/coco.c | 151 +- .../darknet/{src => examples}/darknet.c | 301 +- .../darknet/examples/detector-scipy-opencv.py | 56 + .../inst/include/darknet/examples/detector.c | 850 +++++ .../inst/include/darknet/examples/detector.py | 27 + .../include/darknet/{src => examples}/dice.c | 6 +- .../inst/include/darknet/examples/go.c | 1370 +++++++++ .../darknet/examples/instance-segmenter.c | 267 ++ .../inst/include/darknet/examples/lsd.c | 1378 +++++++++ .../darknet/{src => examples}/nightmare.c | 230 +- .../inst/include/darknet/examples/regressor.c | 240 ++ .../include/darknet/{src => examples}/rnn.c | 204 +- .../darknet/{src => examples}/rnn_vid.c | 11 +- .../inst/include/darknet/examples/segmenter.c | 255 ++ .../include/darknet/{src => examples}/super.c | 41 +- .../include/darknet/{src => examples}/swag.c | 12 +- .../include/darknet/{src => examples}/tag.c | 59 +- .../include/darknet/{src => examples}/voxel.c | 10 +- .../darknet/{src => examples}/writing.c | 12 +- .../include/darknet/{src => examples}/yolo.c | 146 +- .../inst/include/darknet/include/darknet.h | 805 +++++ .../inst/include/darknet/python/darknet.py | 156 + .../inst/include/darknet/python/proverbot.py | 37 + .../darknet/scripts/get_coco_dataset.sh | 31 + .../inst/include/darknet/scripts/voc_label.py | 13 +- .../include/darknet/src/activation_kernels.cu | 87 +- .../include/darknet/src/activation_layer.c | 20 +- .../include/darknet/src/activation_layer.h | 8 +- .../inst/include/darknet/src/activations.c | 7 + .../inst/include/darknet/src/activations.h | 11 +- .../inst/include/darknet/src/avgpool_layer.c | 8 +- .../inst/include/darknet/src/avgpool_layer.h | 8 +- .../darknet/src/avgpool_layer_kernels.cu | 8 +- .../include/darknet/src/batchnorm_layer.c | 200 +- .../include/darknet/src/batchnorm_layer.h | 8 +- image.darknet/inst/include/darknet/src/blas.c | 147 +- image.darknet/inst/include/darknet/src/blas.h | 70 +- .../inst/include/darknet/src/blas_kernels.cu | 408 ++- image.darknet/inst/include/darknet/src/box.c | 168 +- image.darknet/inst/include/darknet/src/box.h | 10 +- .../inst/include/darknet/src/classifier.h | 1 - .../inst/include/darknet/src/col2im.h | 2 +- .../include/darknet/src/col2im_kernels.cu | 2 +- .../inst/include/darknet/src/compare.c | 2 +- .../include/darknet/src/connected_layer.c | 179 +- .../include/darknet/src/connected_layer.h | 22 +- .../darknet/src/convolutional_kernels.cu | 271 +- .../include/darknet/src/convolutional_layer.c | 254 +- .../include/darknet/src/convolutional_layer.h | 18 +- .../inst/include/darknet/src/cost_layer.c | 60 +- .../inst/include/darknet/src/cost_layer.h | 8 +- .../inst/include/darknet/src/crnn_layer.c | 106 +- .../inst/include/darknet/src/crnn_layer.h | 12 +- .../inst/include/darknet/src/crop_layer.c | 10 +- .../inst/include/darknet/src/crop_layer.h | 4 +- .../include/darknet/src/crop_layer_kernels.cu | 32 +- image.darknet/inst/include/darknet/src/cuda.c | 22 +- image.darknet/inst/include/darknet/src/cuda.h | 19 +- image.darknet/inst/include/darknet/src/data.c | 591 +++- image.darknet/inst/include/darknet/src/data.h | 70 +- .../darknet/src/deconvolutional_kernels.cu | 148 +- .../darknet/src/deconvolutional_layer.c | 292 +- .../darknet/src/deconvolutional_layer.h | 29 +- image.darknet/inst/include/darknet/src/demo.c | 416 ++- image.darknet/inst/include/darknet/src/demo.h | 5 +- .../include/darknet/src/detection_layer.c | 88 +- .../include/darknet/src/detection_layer.h | 9 +- .../inst/include/darknet/src/detector.c | 552 ---- .../inst/include/darknet/src/dropout_layer.c | 16 +- .../inst/include/darknet/src/dropout_layer.h | 8 +- .../darknet/src/dropout_layer_kernels.cu | 12 +- image.darknet/inst/include/darknet/src/gemm.c | 62 +- image.darknet/inst/include/darknet/src/gemm.h | 2 +- image.darknet/inst/include/darknet/src/go.c | 833 ----- .../inst/include/darknet/src/gru_layer.c | 461 +-- .../inst/include/darknet/src/gru_layer.h | 14 +- .../inst/include/darknet/src/im2col.h | 2 +- .../include/darknet/src/im2col_kernels.cu | 2 +- .../inst/include/darknet/src/image.c | 561 ++-- .../inst/include/darknet/src/image.h | 66 +- .../inst/include/darknet/src/image_opencv.cpp | 135 + .../inst/include/darknet/src/iseg_layer.c | 225 ++ .../inst/include/darknet/src/iseg_layer.h | 19 + .../inst/include/darknet/src/l2norm_layer.c | 63 + .../inst/include/darknet/src/l2norm_layer.h | 15 + .../inst/include/darknet/src/layer.c | 3 +- .../inst/include/darknet/src/layer.h | 272 +- image.darknet/inst/include/darknet/src/list.h | 15 +- .../inst/include/darknet/src/local_layer.c | 90 +- .../inst/include/darknet/src/local_layer.h | 12 +- .../inst/include/darknet/src/logistic_layer.c | 71 + .../inst/include/darknet/src/logistic_layer.h | 15 + .../inst/include/darknet/src/lstm_layer.c | 626 ++++ .../inst/include/darknet/src/lstm_layer.h | 20 + .../inst/include/darknet/src/matrix.c | 15 + .../inst/include/darknet/src/matrix.h | 13 +- .../inst/include/darknet/src/maxpool_layer.c | 24 +- .../inst/include/darknet/src/maxpool_layer.h | 8 +- .../darknet/src/maxpool_layer_kernels.cu | 24 +- .../inst/include/darknet/src/network.c | 933 ++++-- .../inst/include/darknet/src/network.h | 116 +- .../include/darknet/src/network_kernels.cu | 408 --- .../include/darknet/src/normalization_layer.c | 41 +- .../include/darknet/src/normalization_layer.h | 8 +- .../inst/include/darknet/src/option_list.c | 17 + .../inst/include/darknet/src/option_list.h | 4 - .../inst/include/darknet/src/parser.c | 448 ++- .../inst/include/darknet/src/parser.h | 6 +- .../inst/include/darknet/src/region_layer.c | 396 ++- .../inst/include/darknet/src/region_layer.h | 12 +- .../inst/include/darknet/src/reorg_layer.c | 101 +- .../inst/include/darknet/src/reorg_layer.h | 10 +- .../inst/include/darknet/src/rnn_layer.c | 173 +- .../inst/include/darknet/src/rnn_layer.h | 14 +- .../inst/include/darknet/src/route_layer.c | 21 +- .../inst/include/darknet/src/route_layer.h | 8 +- .../inst/include/darknet/src/shortcut_layer.c | 53 +- .../inst/include/darknet/src/shortcut_layer.h | 9 +- .../inst/include/darknet/src/softmax_layer.c | 67 +- .../inst/include/darknet/src/softmax_layer.h | 8 +- .../inst/include/darknet/src/stb_image.h | 2723 ++++++++++++----- .../include/darknet/src/stb_image_write.h | 1208 ++++++-- image.darknet/inst/include/darknet/src/tree.c | 16 +- image.darknet/inst/include/darknet/src/tree.h | 21 +- .../inst/include/darknet/src/upsample_layer.c | 106 + .../inst/include/darknet/src/upsample_layer.h | 15 + .../inst/include/darknet/src/utils.c | 115 +- .../inst/include/darknet/src/utils.h | 33 +- .../inst/include/darknet/src/yolo_layer.c | 374 +++ .../inst/include/darknet/src/yolo_layer.h | 19 + 193 files changed, 28684 insertions(+), 8626 deletions(-) create mode 100644 image.darknet/inst/include/darknet/LICENSE.fuck create mode 100644 image.darknet/inst/include/darknet/LICENSE.gen create mode 100644 image.darknet/inst/include/darknet/LICENSE.gpl create mode 100644 image.darknet/inst/include/darknet/LICENSE.meta create mode 100644 image.darknet/inst/include/darknet/LICENSE.mit create mode 100644 image.darknet/inst/include/darknet/LICENSE.v1 create mode 100644 image.darknet/inst/include/darknet/Makefile.bak create mode 100644 image.darknet/inst/include/darknet/cfg/darknet53.cfg rename image.darknet/inst/include/darknet/cfg/{msr_50.cfg => darknet53_448.cfg} (85%) rename image.darknet/inst/include/darknet/cfg/{yolo-voc.cfg => darknet9000.cfg} (70%) create mode 100644 image.darknet/inst/include/darknet/cfg/densenet201.cfg create mode 100644 image.darknet/inst/include/darknet/cfg/go.cfg create mode 100644 image.darknet/inst/include/darknet/cfg/imagenet9k.hierarchy.dataset create mode 100644 image.darknet/inst/include/darknet/cfg/openimages.data create mode 100644 image.darknet/inst/include/darknet/cfg/resnet101.cfg rename image.darknet/inst/include/darknet/cfg/{msr_152.cfg => resnet152.cfg} (92%) rename image.darknet/inst/include/darknet/cfg/{yolov1/yolo2.cfg => resnet18.cfg} (58%) rename image.darknet/inst/include/darknet/cfg/{msr_34.cfg => resnet34.cfg} (77%) create mode 100644 image.darknet/inst/include/darknet/cfg/resnet50.cfg create mode 100644 image.darknet/inst/include/darknet/cfg/resnext101-32x4d.cfg create mode 100644 image.darknet/inst/include/darknet/cfg/resnext152-32x4d.cfg create mode 100644 image.darknet/inst/include/darknet/cfg/resnext50.cfg rename image.darknet/inst/include/darknet/cfg/{yolov1/tiny-yolo.cfg => yolov1-tiny.cfg} (94%) rename image.darknet/inst/include/darknet/cfg/{yolov1/yolo.cfg => yolov1.cfg} (98%) delete mode 100644 image.darknet/inst/include/darknet/cfg/yolov1/tiny-coco.cfg delete mode 100644 image.darknet/inst/include/darknet/cfg/yolov1/xyolo.test.cfg delete mode 100644 image.darknet/inst/include/darknet/cfg/yolov1/yolo-coco.cfg delete mode 100644 image.darknet/inst/include/darknet/cfg/yolov1/yolo-small.cfg delete mode 100644 image.darknet/inst/include/darknet/cfg/yolov1/yolo.train.cfg rename image.darknet/inst/include/darknet/cfg/{tiny-yolo-voc.cfg => yolov2-tiny-voc.cfg} (93%) rename image.darknet/inst/include/darknet/cfg/{tiny-yolo.cfg => yolov2-tiny.cfg} (82%) rename image.darknet/inst/include/darknet/cfg/{yolo.cfg => yolov2-voc.cfg} (87%) create mode 100644 image.darknet/inst/include/darknet/cfg/yolov3-openimages.cfg create mode 100644 image.darknet/inst/include/darknet/cfg/yolov3-spp.cfg create mode 100644 image.darknet/inst/include/darknet/cfg/yolov3-tiny.cfg create mode 100644 image.darknet/inst/include/darknet/cfg/yolov3-voc.cfg create mode 100644 image.darknet/inst/include/darknet/data/kite.jpg create mode 100644 image.darknet/inst/include/darknet/data/openimages.names rename image.darknet/inst/include/darknet/{src => examples}/art.c (64%) create mode 100644 image.darknet/inst/include/darknet/examples/attention.c rename image.darknet/inst/include/darknet/{src => examples}/captcha.c (88%) rename image.darknet/inst/include/darknet/{src => examples}/cifar.c (77%) rename image.darknet/inst/include/darknet/{src => examples}/classifier.c (72%) rename image.darknet/inst/include/darknet/{src => examples}/coco.c (72%) rename image.darknet/inst/include/darknet/{src => examples}/darknet.c (62%) create mode 100644 image.darknet/inst/include/darknet/examples/detector-scipy-opencv.py create mode 100644 image.darknet/inst/include/darknet/examples/detector.c create mode 100644 image.darknet/inst/include/darknet/examples/detector.py rename image.darknet/inst/include/darknet/{src => examples}/dice.c (95%) create mode 100644 image.darknet/inst/include/darknet/examples/go.c create mode 100644 image.darknet/inst/include/darknet/examples/instance-segmenter.c create mode 100644 image.darknet/inst/include/darknet/examples/lsd.c rename image.darknet/inst/include/darknet/{src => examples}/nightmare.c (58%) create mode 100644 image.darknet/inst/include/darknet/examples/regressor.c rename image.darknet/inst/include/darknet/{src => examples}/rnn.c (72%) rename image.darknet/inst/include/darknet/{src => examples}/rnn_vid.c (96%) create mode 100644 image.darknet/inst/include/darknet/examples/segmenter.c rename image.darknet/inst/include/darknet/{src => examples}/super.c (78%) rename image.darknet/inst/include/darknet/{src => examples}/swag.c (92%) rename image.darknet/inst/include/darknet/{src => examples}/tag.c (72%) rename image.darknet/inst/include/darknet/{src => examples}/voxel.c (96%) rename image.darknet/inst/include/darknet/{src => examples}/writing.c (90%) rename image.darknet/inst/include/darknet/{src => examples}/yolo.c (68%) create mode 100644 image.darknet/inst/include/darknet/include/darknet.h create mode 100644 image.darknet/inst/include/darknet/python/darknet.py create mode 100644 image.darknet/inst/include/darknet/python/proverbot.py create mode 100644 image.darknet/inst/include/darknet/scripts/get_coco_dataset.sh delete mode 100644 image.darknet/inst/include/darknet/src/detector.c delete mode 100644 image.darknet/inst/include/darknet/src/go.c create mode 100644 image.darknet/inst/include/darknet/src/image_opencv.cpp create mode 100644 image.darknet/inst/include/darknet/src/iseg_layer.c create mode 100644 image.darknet/inst/include/darknet/src/iseg_layer.h create mode 100644 image.darknet/inst/include/darknet/src/l2norm_layer.c create mode 100644 image.darknet/inst/include/darknet/src/l2norm_layer.h create mode 100644 image.darknet/inst/include/darknet/src/logistic_layer.c create mode 100644 image.darknet/inst/include/darknet/src/logistic_layer.h create mode 100644 image.darknet/inst/include/darknet/src/lstm_layer.c create mode 100644 image.darknet/inst/include/darknet/src/lstm_layer.h delete mode 100644 image.darknet/inst/include/darknet/src/network_kernels.cu create mode 100644 image.darknet/inst/include/darknet/src/upsample_layer.c create mode 100644 image.darknet/inst/include/darknet/src/upsample_layer.h create mode 100644 image.darknet/inst/include/darknet/src/yolo_layer.c create mode 100644 image.darknet/inst/include/darknet/src/yolo_layer.h diff --git a/image.darknet/inst/include/darknet/LICENSE.fuck b/image.darknet/inst/include/darknet/LICENSE.fuck new file mode 100644 index 0000000..8b1a9d8 --- /dev/null +++ b/image.darknet/inst/include/darknet/LICENSE.fuck @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/image.darknet/inst/include/darknet/LICENSE.gen b/image.darknet/inst/include/darknet/LICENSE.gen new file mode 100644 index 0000000..c541132 --- /dev/null +++ b/image.darknet/inst/include/darknet/LICENSE.gen @@ -0,0 +1,91 @@ +RNN LICENSE Version 3, June 21 2017 + +Copyright (c) 1990, 1989, 1999 Free87337 May 48 THIRD PARTIES OR ANY OTHER THE +COMPLAIN OR CONSEQUENTIAL DAMAGES AND REGARDLESS OF WHETHER IN CONTRACT, TO THE +EXTENT REPAIR OR AGENTS (NOT THE IN ANY EVENT). THE SOFTWARE WILL BE +UNINTERRUPTED OR ERROR-FREE OR ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF ALL THE WORK (GOVERNED CODE) HIM RESPONSES, OR OF FINES, +SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR ANY OTHER OR OTHER HARL UNDER NO +CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), +PATENT PERMITTED BY THE INSTAGRAM PARENT STATE OR TORT (INCLUDING NEGLIGENCE), +PRODUCT LIABILITY OR OTHERWISE, ARISING OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR ANYTHING PROVIDED IN THIS PRODUCT, COMMIS AND SERVICES +ARE LICENSED SOFTWARE AND ANY RESULE OR ANY OTHER THE COPYRIGHT HOLDERS BE +LIABLE FOR ANY SPECIAL, INCIDENTAL, CASE, SUCH WARRANTIES, EXPRESS OR IMPLIED, +INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COPYRIGHT HOLDERS AND/OR ANY +PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY +EXPRESS OR DISTRIBUTE THAT ALL CLAIMS ARE SHALL CREATE DERAVE BE LIABLE TO YOU +WILL HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +6\. TERMINATION. TO THE EXTENT PERMITTED BY LAW, NO USE OF THE COVERED CODE IS +WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE +INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +SERVICING, REPAIR OR COULT OR IN ANY WAY OUT OF THE USE OF THE WEBSITES OR +SERVICE WILL BE CONSEQUENTIAL DAMAGES OF ANY KIND HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + +This paragraph Agreement constitutes the entire agreement between the parties +with respect to the Work licensed here. However, if you place the name of the +fact that the arbitration was the consultation of the parties as a "patent is". +Subject to the terms and conditions of this License, Contributor has knowledge +that a license under a third party may also be used to endorse or promote +products derived from the Work, and there is no warranty on the Software and +Science Fees. For the purposes of this Agreement, attach the following +disclaimers (without liabilities of written notice to the Subject Software) in a +manner that a product is under common control with you. The Free Software +Foundation may publish revised and/or new versions of the License for the +Modifications made by the applicable terms. The Recipient shall promptly retain +the covered works for any reason be entered in any federal or state or login +Restricted Laws appearing in the United States or any of its own information +that is not disabled from a derivative work except as expressly permitted in +this License, to the extent that they are in receiving the Software and Source +Code or any exercise of the rights granted to You by this License or a +Contributor made by the Licensor or are authorized to make a reasonable +retirement by the courts of the courts located in Santa Clara County, California +printed and related to the Work or “Company” and Apache Software Foundation. If +the Licensor shall be entitled to reflect your rights to use the Software and +the Software to exercise the rights granted to the recipient without a +requirement to exercise the rights granted by the Agreement to the provision +will begin will appear in such cases, you will use such information without such +corporation shall be an officer with respect to any part of the Software or any +portion thereof. Capitalized terms are included in the Initial Contributor and +under no circumstances will license the Service at any time and for any direct, +indirect, special, incidental, or consequential damages of or assist in +connection with any Services or the registration purposes only to the extent +that it includes any or all means including the processing of which you download +any derivative work. Any of the purchases’ transmission purposes are made +available, if any, in other circumstances, we may review the copyright notice. +In the event that this Agreement is required to give us strict content. The +inclusion of the other party hereunder may also notify you Intellectual Property +Rights to any third party. This means that the Source Code exists of the Work +will not charge a program available to you at any time. You must include a +prominent statement that the Software is governed under a particular version of +this Agreement. You must include a provision to the extent that there is no +warranty for the content of others. You agree that the Recipient was appointed +as a Contributor, (c) are effective until terminated by hereunder, then the +registration are not disabled and not limited to, submit any Customer Data +without the updated use of the Software and that no fee is released. You grant +to Use Other Arbitration Rules for Diagnostic or Services may use or modify the +Apple Software and Consolidated Apple Software or Services. The Company may have +full risk as a product of the Compatible Source. A Contribution by the Licensor +or by the updated Software under the following conditions we can redistribute +any General Provision of this Agreement. If the Program is used in accordance +with the terms of this Agreement, Customer may provide advertisements from your +devices that clause you can your employer or a transaction or country that has +been controlled by the arbitrator, that they will be useful of this Agreement. +The term "Open Source Software is available in connection with the program, and +you may not protect the combination of the Covered Code. You should like to +select a user's rights to charge a copy of this License. I are Contributor's +confidentiality of the exercise of the rights granted herein. Such a covered +work is released as a consequence, the Licensor shall be eligible for a purpose +or subcontractor of the person or entity to the user of the user, then the word +"Application" means having the original fee for any reason; and that no patent +license to more than fifty stated close of the license term. The terms of this +License will the license terms and conditions set forth in Section 2.2 (OPEC) +and You will not use the Software or any set of responsibility for any resulting +information that the Original Code warrants that you have the right to disclose +these information (or in the notification; or (iii) late use of the software or +any third party to the three (50) days before such belief to the extent that it +includes a court court obtains the rights granted by this License. diff --git a/image.darknet/inst/include/darknet/LICENSE.gpl b/image.darknet/inst/include/darknet/LICENSE.gpl new file mode 100644 index 0000000..9cecc1d --- /dev/null +++ b/image.darknet/inst/include/darknet/LICENSE.gpl @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/image.darknet/inst/include/darknet/LICENSE.meta b/image.darknet/inst/include/darknet/LICENSE.meta new file mode 100644 index 0000000..6728bd2 --- /dev/null +++ b/image.darknet/inst/include/darknet/LICENSE.meta @@ -0,0 +1,8 @@ + META-LICENSE + Version 1, June 21 2017 + +Any and all licenses may be applied to the software either individually +or in concert. Any issues, ambiguities, paradoxes, or metaphysical quandries +arising from this combination should be discussed with a local faith leader, +hermit, or guru. The Oxford comma shall be used. + diff --git a/image.darknet/inst/include/darknet/LICENSE.mit b/image.darknet/inst/include/darknet/LICENSE.mit new file mode 100644 index 0000000..5bd806c --- /dev/null +++ b/image.darknet/inst/include/darknet/LICENSE.mit @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2017 Joseph Redmon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/image.darknet/inst/include/darknet/LICENSE.v1 b/image.darknet/inst/include/darknet/LICENSE.v1 new file mode 100644 index 0000000..5b8709a --- /dev/null +++ b/image.darknet/inst/include/darknet/LICENSE.v1 @@ -0,0 +1,13 @@ + YOLO LICENSE + Version 1, July 10 2015 + +THIS SOFTWARE LICENSE IS PROVIDED "ALL CAPS" SO THAT YOU KNOW IT IS SUPER +SERIOUS AND YOU DON'T MESS AROUND WITH COPYRIGHT LAW BECAUSE YOU WILL GET IN +TROUBLE HERE ARE SOME OTHER BUZZWORDS COMMONLY IN THESE THINGS WARRANTIES +LIABILITY CONTRACT TORT LIABLE CLAIMS RESTRICTION MERCHANTABILITY SUBJECT TO +THE FOLLOWING CONDITIONS: + +1. #yolo +2. #swag +3. #blazeit + diff --git a/image.darknet/inst/include/darknet/Makefile b/image.darknet/inst/include/darknet/Makefile index 3d3d5e4..63e15e6 100644 --- a/image.darknet/inst/include/darknet/Makefile +++ b/image.darknet/inst/include/darknet/Makefile @@ -1,27 +1,37 @@ GPU=0 CUDNN=0 OPENCV=0 +OPENMP=0 DEBUG=0 -ARCH= -gencode arch=compute_20,code=[sm_20,sm_21] \ - -gencode arch=compute_30,code=sm_30 \ +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 +# ARCH= -gencode arch=compute_52,code=compute_52 -VPATH=./src/ +VPATH=./src/:./examples +SLIB=libdarknet.so +ALIB=libdarknet.a EXEC=darknet OBJDIR=./obj/ CC=gcc +CPP=g++ NVCC=nvcc +AR=ar +ARFLAGS=rcs OPTS=-Ofast LDFLAGS= -lm -pthread -COMMON= -CFLAGS=-Wall -Wfatal-errors +COMMON= -Iinclude/ -Isrc/ +CFLAGS=-Wall -Wno-unused-result -Wno-unknown-pragmas -Wfatal-errors -fPIC + +ifeq ($(OPENMP), 1) +CFLAGS+= -fopenmp +endif ifeq ($(DEBUG), 1) OPTS=-O0 -g @@ -32,7 +42,7 @@ CFLAGS+=$(OPTS) ifeq ($(OPENCV), 1) COMMON+= -DOPENCV CFLAGS+= -DOPENCV -LDFLAGS+= `pkg-config --libs opencv` +LDFLAGS+= `pkg-config --libs opencv` -lstdc++ COMMON+= `pkg-config --cflags opencv` endif @@ -48,19 +58,32 @@ CFLAGS+= -DCUDNN LDFLAGS+= -lcudnn endif -OBJ=gemm.o utils.o cuda.o convolutional_layer.o list.o image.o activations.o im2col.o col2im.o blas.o crop_layer.o dropout_layer.o maxpool_layer.o softmax_layer.o data.o matrix.o network.o connected_layer.o cost_layer.o parser.o option_list.o darknet.o detection_layer.o captcha.o route_layer.o writing.o box.o nightmare.o normalization_layer.o avgpool_layer.o coco.o dice.o yolo.o detector.o layer.o compare.o classifier.o local_layer.o swag.o shortcut_layer.o activation_layer.o rnn_layer.o gru_layer.o rnn.o rnn_vid.o crnn_layer.o demo.o tag.o cifar.o go.o batchnorm_layer.o art.o region_layer.o reorg_layer.o super.o voxel.o tree.o +OBJ=gemm.o utils.o cuda.o deconvolutional_layer.o convolutional_layer.o list.o image.o activations.o im2col.o col2im.o blas.o crop_layer.o dropout_layer.o maxpool_layer.o softmax_layer.o data.o matrix.o network.o connected_layer.o cost_layer.o parser.o option_list.o detection_layer.o route_layer.o upsample_layer.o box.o normalization_layer.o avgpool_layer.o layer.o local_layer.o shortcut_layer.o logistic_layer.o activation_layer.o rnn_layer.o gru_layer.o crnn_layer.o demo.o batchnorm_layer.o region_layer.o reorg_layer.o tree.o lstm_layer.o l2norm_layer.o yolo_layer.o iseg_layer.o image_opencv.o +EXECOBJA=captcha.o lsd.o super.o art.o tag.o cifar.o go.o rnn.o segmenter.o regressor.o classifier.o coco.o yolo.o detector.o nightmare.o instance-segmenter.o darknet.o ifeq ($(GPU), 1) LDFLAGS+= -lstdc++ -OBJ+=convolutional_kernels.o activation_kernels.o im2col_kernels.o col2im_kernels.o blas_kernels.o crop_layer_kernels.o dropout_layer_kernels.o maxpool_layer_kernels.o network_kernels.o avgpool_layer_kernels.o +OBJ+=convolutional_kernels.o deconvolutional_kernels.o activation_kernels.o im2col_kernels.o col2im_kernels.o blas_kernels.o crop_layer_kernels.o dropout_layer_kernels.o maxpool_layer_kernels.o avgpool_layer_kernels.o endif +EXECOBJ = $(addprefix $(OBJDIR), $(EXECOBJA)) OBJS = $(addprefix $(OBJDIR), $(OBJ)) -DEPS = $(wildcard src/*.h) Makefile +DEPS = $(wildcard src/*.h) Makefile include/darknet.h + +all: obj backup results $(SLIB) $(ALIB) $(EXEC) +#all: obj results $(SLIB) $(ALIB) $(EXEC) + + +$(EXEC): $(EXECOBJ) $(ALIB) + $(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(ALIB) + +$(ALIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $^ -all: obj backup results $(EXEC) +$(SLIB): $(OBJS) + $(CC) $(CFLAGS) -shared $^ -o $@ $(LDFLAGS) -$(EXEC): $(OBJS) - $(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS) +$(OBJDIR)%.o: %.cpp $(DEPS) + $(CPP) $(COMMON) $(CFLAGS) -c $< -o $@ $(OBJDIR)%.o: %.c $(DEPS) $(CC) $(COMMON) $(CFLAGS) -c $< -o $@ @@ -78,5 +101,5 @@ results: .PHONY: clean clean: - rm -rf $(OBJS) $(EXEC) + rm -rf $(OBJS) $(SLIB) $(ALIB) $(EXEC) $(EXECOBJ) $(OBJDIR)/* diff --git a/image.darknet/inst/include/darknet/Makefile.bak b/image.darknet/inst/include/darknet/Makefile.bak new file mode 100644 index 0000000..3d3d5e4 --- /dev/null +++ b/image.darknet/inst/include/darknet/Makefile.bak @@ -0,0 +1,82 @@ +GPU=0 +CUDNN=0 +OPENCV=0 +DEBUG=0 + +ARCH= -gencode arch=compute_20,code=[sm_20,sm_21] \ + -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] + +# This is what I use, uncomment if you know your arch and want to specify +# ARCH= -gencode arch=compute_52,code=compute_52 + +VPATH=./src/ +EXEC=darknet +OBJDIR=./obj/ + +CC=gcc +NVCC=nvcc +OPTS=-Ofast +LDFLAGS= -lm -pthread +COMMON= +CFLAGS=-Wall -Wfatal-errors + +ifeq ($(DEBUG), 1) +OPTS=-O0 -g +endif + +CFLAGS+=$(OPTS) + +ifeq ($(OPENCV), 1) +COMMON+= -DOPENCV +CFLAGS+= -DOPENCV +LDFLAGS+= `pkg-config --libs opencv` +COMMON+= `pkg-config --cflags opencv` +endif + +ifeq ($(GPU), 1) +COMMON+= -DGPU -I/usr/local/cuda/include/ +CFLAGS+= -DGPU +LDFLAGS+= -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand +endif + +ifeq ($(CUDNN), 1) +COMMON+= -DCUDNN +CFLAGS+= -DCUDNN +LDFLAGS+= -lcudnn +endif + +OBJ=gemm.o utils.o cuda.o convolutional_layer.o list.o image.o activations.o im2col.o col2im.o blas.o crop_layer.o dropout_layer.o maxpool_layer.o softmax_layer.o data.o matrix.o network.o connected_layer.o cost_layer.o parser.o option_list.o darknet.o detection_layer.o captcha.o route_layer.o writing.o box.o nightmare.o normalization_layer.o avgpool_layer.o coco.o dice.o yolo.o detector.o layer.o compare.o classifier.o local_layer.o swag.o shortcut_layer.o activation_layer.o rnn_layer.o gru_layer.o rnn.o rnn_vid.o crnn_layer.o demo.o tag.o cifar.o go.o batchnorm_layer.o art.o region_layer.o reorg_layer.o super.o voxel.o tree.o +ifeq ($(GPU), 1) +LDFLAGS+= -lstdc++ +OBJ+=convolutional_kernels.o activation_kernels.o im2col_kernels.o col2im_kernels.o blas_kernels.o crop_layer_kernels.o dropout_layer_kernels.o maxpool_layer_kernels.o network_kernels.o avgpool_layer_kernels.o +endif + +OBJS = $(addprefix $(OBJDIR), $(OBJ)) +DEPS = $(wildcard src/*.h) Makefile + +all: obj backup results $(EXEC) + +$(EXEC): $(OBJS) + $(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +$(OBJDIR)%.o: %.c $(DEPS) + $(CC) $(COMMON) $(CFLAGS) -c $< -o $@ + +$(OBJDIR)%.o: %.cu $(DEPS) + $(NVCC) $(ARCH) $(COMMON) --compiler-options "$(CFLAGS)" -c $< -o $@ + +obj: + mkdir -p obj +backup: + mkdir -p backup +results: + mkdir -p results + +.PHONY: clean + +clean: + rm -rf $(OBJS) $(EXEC) + diff --git a/image.darknet/inst/include/darknet/README.md b/image.darknet/inst/include/darknet/README.md index d255dab..09fdeee 100644 --- a/image.darknet/inst/include/darknet/README.md +++ b/image.darknet/inst/include/darknet/README.md @@ -1,6 +1,6 @@ ![Darknet Logo](http://pjreddie.com/media/files/darknet-black-small.png) -#Darknet# +# Darknet # Darknet is an open source neural network framework written in C and CUDA. It is fast, easy to install, and supports CPU and GPU computation. For more information see the [Darknet project website](http://pjreddie.com/darknet). diff --git a/image.darknet/inst/include/darknet/cfg/alexnet.cfg b/image.darknet/inst/include/darknet/cfg/alexnet.cfg index 7e5a9b2..e2ed4bb 100644 --- a/image.darknet/inst/include/darknet/cfg/alexnet.cfg +++ b/image.darknet/inst/include/darknet/cfg/alexnet.cfg @@ -1,5 +1,9 @@ [net] -batch=128 +# Training +# batch=128 +# subdivisions=1 +# Testing +batch=1 subdivisions=1 height=227 width=227 @@ -90,6 +94,3 @@ activation=linear [softmax] groups=1 -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/cifar.cfg b/image.darknet/inst/include/darknet/cfg/cifar.cfg index f2c801a..b2f69f5 100644 --- a/image.darknet/inst/include/darknet/cfg/cifar.cfg +++ b/image.darknet/inst/include/darknet/cfg/cifar.cfg @@ -1,25 +1,23 @@ [net] batch=128 subdivisions=1 -height=32 -width=32 +height=28 +width=28 channels=3 -momentum=0.9 -decay=0.0005 +max_crop=32 +min_crop=32 + +hue=.1 +saturation=.75 +exposure=.75 learning_rate=0.4 policy=poly power=4 -max_batches = 50000 +max_batches = 5000 +momentum=0.9 +decay=0.0005 -[crop] -crop_width=28 -crop_height=28 -flip=1 -angle=0 -saturation = 1 -exposure = 1 -noadjust=1 [convolutional] batch_normalize=1 @@ -121,6 +119,3 @@ activation=leaky [softmax] groups=1 - -[cost] - diff --git a/image.darknet/inst/include/darknet/cfg/cifar.test.cfg b/image.darknet/inst/include/darknet/cfg/cifar.test.cfg index d3afcdd..18b6c54 100644 --- a/image.darknet/inst/include/darknet/cfg/cifar.test.cfg +++ b/image.darknet/inst/include/darknet/cfg/cifar.test.cfg @@ -115,5 +115,3 @@ activation=leaky groups=1 temperature=3 -[cost] - diff --git a/image.darknet/inst/include/darknet/cfg/coco.data b/image.darknet/inst/include/darknet/cfg/coco.data index 610151d..3003841 100644 --- a/image.darknet/inst/include/darknet/cfg/coco.data +++ b/image.darknet/inst/include/darknet/cfg/coco.data @@ -1,7 +1,7 @@ classes= 80 train = /home/pjreddie/data/coco/trainvalno5k.txt -#valid = coco_testdev -valid = data/coco_val_5k.list +valid = coco_testdev +#valid = data/coco_val_5k.list names = data/coco.names backup = /home/pjreddie/backup/ eval=coco diff --git a/image.darknet/inst/include/darknet/cfg/darknet.cfg b/image.darknet/inst/include/darknet/cfg/darknet.cfg index 60b939a..375107f 100644 --- a/image.darknet/inst/include/darknet/cfg/darknet.cfg +++ b/image.darknet/inst/include/darknet/cfg/darknet.cfg @@ -1,17 +1,30 @@ [net] -batch=128 +# Training +# batch=128 +# subdivisions=1 +# Testing +batch=1 subdivisions=1 -height=224 -width=224 +height=256 +width=256 +min_crop=128 +max_crop=448 channels=3 momentum=0.9 decay=0.0005 -max_crop=320 +burn_in=1000 learning_rate=0.1 policy=poly power=4 -max_batches=1600000 +max_batches=800000 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 + [convolutional] batch_normalize=1 @@ -84,7 +97,6 @@ activation=leaky [maxpool] size=2 stride=2 -padding=1 [convolutional] batch_normalize=1 @@ -94,18 +106,15 @@ stride=1 pad=1 activation=leaky +[avgpool] + [convolutional] filters=1000 size=1 stride=1 pad=1 -activation=leaky - -[avgpool] +activation=linear [softmax] groups=1 -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/darknet19.cfg b/image.darknet/inst/include/darknet/cfg/darknet19.cfg index bf73fb7..28ac966 100644 --- a/image.darknet/inst/include/darknet/cfg/darknet19.cfg +++ b/image.darknet/inst/include/darknet/cfg/darknet19.cfg @@ -1,17 +1,31 @@ [net] -batch=128 -subdivisions=1 -height=224 -width=224 +# Training +#batch=128 +#subdivisions=2 + +# Testing + batch=1 + subdivisions=1 + +height=256 +width=256 +min_crop=128 +max_crop=448 channels=3 momentum=0.9 decay=0.0005 -max_crop=448 +burn_in=1000 learning_rate=0.1 policy=poly power=4 -max_batches=1600000 +max_batches=800000 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 [convolutional] batch_normalize=1 @@ -189,6 +203,3 @@ activation=linear [softmax] groups=1 -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/darknet19_448.cfg b/image.darknet/inst/include/darknet/cfg/darknet19_448.cfg index 133c688..c6df730 100644 --- a/image.darknet/inst/include/darknet/cfg/darknet19_448.cfg +++ b/image.darknet/inst/include/darknet/cfg/darknet19_448.cfg @@ -195,6 +195,3 @@ activation=linear [softmax] groups=1 -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/darknet53.cfg b/image.darknet/inst/include/darknet/cfg/darknet53.cfg new file mode 100644 index 0000000..7b6d576 --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/darknet53.cfg @@ -0,0 +1,566 @@ +[net] +# Training +# batch=128 +# subdivisions=4 + +# Testing +batch=1 +subdivisions=1 + +height=256 +width=256 +channels=3 +min_crop=128 +max_crop=448 + +burn_in=1000 +learning_rate=0.1 +policy=poly +power=4 +max_batches=800000 +momentum=0.9 +decay=0.0005 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 + + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +# Downsample + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[avgpool] + +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear + +[softmax] +groups=1 + diff --git a/image.darknet/inst/include/darknet/cfg/msr_50.cfg b/image.darknet/inst/include/darknet/cfg/darknet53_448.cfg similarity index 85% rename from image.darknet/inst/include/darknet/cfg/msr_50.cfg rename to image.darknet/inst/include/darknet/cfg/darknet53_448.cfg index 2edd21c..dedab1b 100644 --- a/image.darknet/inst/include/darknet/cfg/msr_50.cfg +++ b/image.darknet/inst/include/darknet/cfg/darknet53_448.cfg @@ -1,48 +1,47 @@ [net] -batch=128 -subdivisions=8 -height=256 -width=256 +# Training - start training with darknet53.weights +# batch=128 +# subdivisions=8 + +# Testing +batch=1 +subdivisions=1 + +height=448 +width=448 channels=3 -momentum=0.9 -decay=0.0001 +min_crop=448 +max_crop=512 -learning_rate=0.05 +learning_rate=0.001 policy=poly power=4 -max_batches=500000 - +max_batches=100000 +momentum=0.9 +decay=0.0005 -[crop] -crop_height=224 -crop_width=224 -flip=1 -saturation=1 -exposure=1 -angle=0 +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky -##### Conv 1 ##### +# Downsample [convolutional] batch_normalize=1 filters=64 -size=7 +size=3 stride=2 pad=1 activation=leaky -[maxpool] -size=3 -stride=2 - - -##### Conv 2_x ##### - - [convolutional] batch_normalize=1 -filters=64 +filters=32 size=1 stride=1 pad=1 @@ -56,27 +55,18 @@ stride=1 pad=1 activation=leaky -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 +[shortcut] +from=-3 activation=linear -[route] -layers=-4 +# Downsample [convolutional] batch_normalize=1 -size=1 -stride=1 +filters=128 +size=3 +stride=2 pad=1 -activation=linear -filters=256 - -[shortcut] -from = -3 activation=leaky [convolutional] @@ -89,54 +79,65 @@ activation=leaky [convolutional] batch_normalize=1 -filters=64 +filters=128 size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + [convolutional] batch_normalize=1 -filters=256 +filters=64 size=1 stride=1 pad=1 -activation=linear - -[shortcut] -from = -4 activation=leaky [convolutional] batch_normalize=1 -filters=64 -size=1 +filters=128 +size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + +# Downsample + [convolutional] batch_normalize=1 -filters=64 +filters=256 size=3 -stride=1 +stride=2 pad=1 activation=leaky [convolutional] batch_normalize=1 -filters=256 +filters=128 size=1 stride=1 pad=1 -activation=linear - -[shortcut] -from = -4 activation=leaky +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky -##### Conv 3_x ##### +[shortcut] +from=-3 +activation=linear [convolutional] batch_normalize=1 @@ -148,34 +149,35 @@ activation=leaky [convolutional] batch_normalize=1 -filters=128 +filters=256 size=3 -stride=2 +stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + [convolutional] batch_normalize=1 -filters=512 +filters=128 size=1 stride=1 pad=1 -activation=linear - -[route] -layers=-4 +activation=leaky [convolutional] batch_normalize=1 -size=1 -stride=2 +filters=256 +size=3 +stride=1 pad=1 -activation=linear -filters=512 +activation=leaky [shortcut] -from = -3 -activation=leaky +from=-3 +activation=linear [convolutional] batch_normalize=1 @@ -187,51 +189,56 @@ activation=leaky [convolutional] batch_normalize=1 -filters=128 +filters=256 size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + + [convolutional] batch_normalize=1 -filters=512 +filters=128 size=1 stride=1 pad=1 -activation=linear - -[shortcut] -from = -4 activation=leaky [convolutional] batch_normalize=1 -filters=128 -size=1 +filters=256 +size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + [convolutional] batch_normalize=1 filters=128 -size=3 +size=1 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 -filters=512 -size=1 +filters=256 +size=3 stride=1 pad=1 -activation=linear +activation=leaky [shortcut] -from = -4 -activation=leaky +from=-3 +activation=linear [convolutional] batch_normalize=1 @@ -243,38 +250,41 @@ activation=leaky [convolutional] batch_normalize=1 -filters=128 +filters=256 size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + [convolutional] batch_normalize=1 -filters=512 +filters=128 size=1 stride=1 pad=1 -activation=linear - -[shortcut] -from = -4 activation=leaky - -##### Conv 4_x ##### - [convolutional] batch_normalize=1 filters=256 -size=1 +size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + +# Downsample + [convolutional] batch_normalize=1 -filters=256 +filters=512 size=3 stride=2 pad=1 @@ -282,26 +292,23 @@ activation=leaky [convolutional] batch_normalize=1 -filters=1024 +filters=256 size=1 stride=1 pad=1 -activation=linear - -[route] -layers=-4 +activation=leaky [convolutional] batch_normalize=1 -size=1 -stride=2 +filters=512 +size=3 +stride=1 pad=1 -activation=linear -filters=1024 +activation=leaky [shortcut] -from = -3 -activation=leaky +from=-3 +activation=linear [convolutional] @@ -314,23 +321,16 @@ activation=leaky [convolutional] batch_normalize=1 -filters=256 +filters=512 size=3 stride=1 pad=1 activation=leaky -[convolutional] -batch_normalize=1 -filters=1024 -size=1 -stride=1 -pad=1 +[shortcut] +from=-3 activation=linear -[shortcut] -from = -4 -activation=leaky [convolutional] batch_normalize=1 @@ -342,23 +342,16 @@ activation=leaky [convolutional] batch_normalize=1 -filters=256 +filters=512 size=3 stride=1 pad=1 activation=leaky -[convolutional] -batch_normalize=1 -filters=1024 -size=1 -stride=1 -pad=1 +[shortcut] +from=-3 activation=linear -[shortcut] -from = -4 -activation=leaky [convolutional] batch_normalize=1 @@ -370,51 +363,57 @@ activation=leaky [convolutional] batch_normalize=1 -filters=256 +filters=512 size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + [convolutional] batch_normalize=1 -filters=1024 +filters=256 size=1 stride=1 pad=1 -activation=linear - -[shortcut] -from = -4 activation=leaky [convolutional] batch_normalize=1 -filters=256 -size=1 +filters=512 +size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + + [convolutional] batch_normalize=1 filters=256 -size=3 +size=1 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 -filters=1024 -size=1 +filters=512 +size=3 stride=1 pad=1 -activation=linear +activation=leaky [shortcut] -from = -4 -activation=leaky +from=-3 +activation=linear + [convolutional] batch_normalize=1 @@ -426,38 +425,41 @@ activation=leaky [convolutional] batch_normalize=1 -filters=256 +filters=512 size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + [convolutional] batch_normalize=1 -filters=1024 +filters=256 size=1 stride=1 pad=1 -activation=linear - -[shortcut] -from = -4 activation=leaky - -##### Conv 5_x ##### - [convolutional] batch_normalize=1 filters=512 -size=1 +size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + +# Downsample + [convolutional] batch_normalize=1 -filters=512 +filters=1024 size=3 stride=2 pad=1 @@ -465,28 +467,23 @@ activation=leaky [convolutional] batch_normalize=1 -filters=2048 +filters=512 size=1 stride=1 pad=1 -activation=linear - - -[route] -layers=-4 +activation=leaky [convolutional] batch_normalize=1 -size=1 -stride=2 +filters=1024 +size=3 +stride=1 pad=1 -activation=linear -filters=2048 - -[shortcut] -from = -3 activation=leaky +[shortcut] +from=-3 +activation=linear [convolutional] batch_normalize=1 @@ -498,61 +495,65 @@ activation=leaky [convolutional] batch_normalize=1 -filters=512 +filters=1024 size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + [convolutional] batch_normalize=1 -filters=2048 +filters=512 size=1 stride=1 pad=1 -activation=linear - -[shortcut] -from = -4 activation=leaky [convolutional] batch_normalize=1 -filters=512 -size=1 +filters=1024 +size=3 stride=1 pad=1 activation=leaky +[shortcut] +from=-3 +activation=linear + [convolutional] batch_normalize=1 filters=512 -size=3 +size=1 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 -filters=2048 -size=1 +filters=1024 +size=3 stride=1 pad=1 -activation=linear +activation=leaky [shortcut] -from = -4 -activation=leaky +from=-3 +activation=linear [avgpool] -[connected] -output=1000 -activation=leaky +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear [softmax] groups=1 -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/yolo-voc.cfg b/image.darknet/inst/include/darknet/cfg/darknet9000.cfg similarity index 70% rename from image.darknet/inst/include/darknet/cfg/yolo-voc.cfg rename to image.darknet/inst/include/darknet/cfg/darknet9000.cfg index ceb3f2a..9dd2dfb 100644 --- a/image.darknet/inst/include/darknet/cfg/yolo-voc.cfg +++ b/image.darknet/inst/include/darknet/cfg/darknet9000.cfg @@ -1,21 +1,27 @@ [net] -batch=64 -subdivisions=8 -height=416 -width=416 +# Training +# batch=128 +# subdivisions=4 +# Testing +batch = 1 +subdivisions = 1 +height=448 +width=448 +max_crop=512 channels=3 momentum=0.9 decay=0.0005 -angle=0 -saturation = 1.5 -exposure = 1.5 -hue=.1 -learning_rate=0.0001 -max_batches = 45000 -policy=steps -steps=100,25000,35000 -scales=10,.1,.1 +learning_rate=0.001 +policy=poly +power=4 +max_batches=100000 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 [convolutional] batch_normalize=1 @@ -181,64 +187,19 @@ stride=1 pad=1 activation=leaky - -####### - [convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -batch_normalize=1 -size=3 +filters=9418 +size=1 stride=1 pad=1 -filters=1024 -activation=leaky +activation=linear -[route] -layers=-9 +[avgpool] -[reorg] -stride=2 +[softmax] +groups=1 +tree=data/9k.tree -[route] -layers=-1,-3 - -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -size=1 -stride=1 -pad=1 -filters=125 -activation=linear +[cost] +type=masked -[region] -anchors = 1.08,1.19, 3.42,4.41, 6.63,11.38, 9.42,5.11, 16.62,10.52 -bias_match=1 -classes=20 -coords=4 -num=5 -softmax=1 -jitter=.2 -rescore=1 - -object_scale=5 -noobject_scale=1 -class_scale=1 -coord_scale=1 - -absolute=1 -thresh = .6 -random=0 diff --git a/image.darknet/inst/include/darknet/cfg/densenet201.cfg b/image.darknet/inst/include/darknet/cfg/densenet201.cfg new file mode 100644 index 0000000..65b4aec --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/densenet201.cfg @@ -0,0 +1,1951 @@ +[net] +# Training +# batch=128 +# subdivisions=4 + +# Testing +batch=1 +subdivisions=1 + +height=256 +width=256 +max_crop=448 +channels=3 +momentum=0.9 +decay=0.0005 + +burn_in=1000 +learning_rate=0.1 +policy=poly +power=4 +max_batches=1600000 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 + +[convolutional] +batch_normalize=1 +filters=64 +size=7 +stride=2 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[route] +layers=-1,-3 + + +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear + +[avgpool] + +[softmax] +groups=1 + diff --git a/image.darknet/inst/include/darknet/cfg/extraction.cfg b/image.darknet/inst/include/darknet/cfg/extraction.cfg index 94e1067..66cb15f 100644 --- a/image.darknet/inst/include/darknet/cfg/extraction.cfg +++ b/image.darknet/inst/include/darknet/cfg/extraction.cfg @@ -1,6 +1,12 @@ [net] -batch=128 +# Training +# batch=128 +# subdivisions=4 + +# Testing +batch=1 subdivisions=1 + height=224 width=224 max_crop=320 @@ -201,6 +207,3 @@ activation=leaky [softmax] groups=1 -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/extraction22k.cfg b/image.darknet/inst/include/darknet/cfg/extraction22k.cfg index 4cec6da..b5f5409 100644 --- a/image.darknet/inst/include/darknet/cfg/extraction22k.cfg +++ b/image.darknet/inst/include/darknet/cfg/extraction22k.cfg @@ -204,6 +204,3 @@ activation=leaky [softmax] groups=1 -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/go.cfg b/image.darknet/inst/include/darknet/cfg/go.cfg new file mode 100644 index 0000000..c730092 --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/go.cfg @@ -0,0 +1,132 @@ +[net] +batch=512 +subdivisions=1 +height=19 +width=19 +channels=1 +momentum=0.9 +decay=0.0005 + +burn_in=1000 +learning_rate=0.1 +policy=poly +power=4 +max_batches=10000000 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=256 +size=3 +stride=1 +pad=1 +activation=relu +batch_normalize=1 + +[convolutional] +filters=1 +size=1 +stride=1 +pad=1 +activation=linear + +[reorg] +extra=1 +stride=1 + +[softmax] + diff --git a/image.darknet/inst/include/darknet/cfg/go.test.cfg b/image.darknet/inst/include/darknet/cfg/go.test.cfg index 6b92d33..1e4e438 100644 --- a/image.darknet/inst/include/darknet/cfg/go.test.cfg +++ b/image.darknet/inst/include/darknet/cfg/go.test.cfg @@ -7,13 +7,13 @@ channels=1 momentum=0.9 decay=0.0005 -learning_rate=0.1 +learning_rate=0.01 policy=poly power=4 -max_batches=400000 +max_batches=100000 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -21,7 +21,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -29,7 +29,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -37,7 +37,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -45,7 +45,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -53,7 +53,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -61,7 +61,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -69,7 +69,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -77,7 +77,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -85,7 +85,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -93,7 +93,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -101,7 +101,7 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 @@ -109,14 +109,13 @@ activation=relu batch_normalize=1 [convolutional] -filters=192 +filters=256 size=3 stride=1 pad=1 activation=relu batch_normalize=1 - [convolutional] filters=1 size=1 @@ -124,8 +123,10 @@ stride=1 pad=1 activation=linear +[reorg] +extra=1 +stride=1 + [softmax] -[cost] -type=sse diff --git a/image.darknet/inst/include/darknet/cfg/gru.cfg b/image.darknet/inst/include/darknet/cfg/gru.cfg index f9a0699..6064221 100644 --- a/image.darknet/inst/include/darknet/cfg/gru.cfg +++ b/image.darknet/inst/include/darknet/cfg/gru.cfg @@ -1,27 +1,25 @@ [net] -subdivisions=1 inputs=256 -batch = 1 momentum=0.9 -decay=0.001 +decay=0.0 +subdivisions=1 +batch = 1 time_steps=1 -learning_rate=0.5 +learning_rate=.002 +adam=1 -policy=poly +policy=constant power=4 -max_batches=2000 +max_batches=1000000 [gru] -batch_normalize=1 -output = 1024 +output = 256 [gru] -batch_normalize=1 -output = 1024 +output = 256 [gru] -batch_normalize=1 -output = 1024 +output = 256 [connected] output=256 @@ -29,6 +27,3 @@ activation=linear [softmax] -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/imagenet22k.dataset b/image.darknet/inst/include/darknet/cfg/imagenet22k.dataset index 920785d..e25ef00 100644 --- a/image.darknet/inst/include/darknet/cfg/imagenet22k.dataset +++ b/image.darknet/inst/include/darknet/cfg/imagenet22k.dataset @@ -1,6 +1,7 @@ classes=21842 train = /data/imagenet/imagenet22k.train.list valid = /data/imagenet/imagenet22k.valid.list +#valid = /data/imagenet/imagenet1k.valid.list backup = /home/pjreddie/backup/ labels = data/imagenet.labels.list names = data/imagenet.shortnames.list diff --git a/image.darknet/inst/include/darknet/cfg/imagenet9k.hierarchy.dataset b/image.darknet/inst/include/darknet/cfg/imagenet9k.hierarchy.dataset new file mode 100644 index 0000000..41fb71b --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/imagenet9k.hierarchy.dataset @@ -0,0 +1,9 @@ +classes=9418 +train = data/9k.train.list +valid = /data/imagenet/imagenet1k.valid.list +leaves = data/imagenet1k.labels +backup = /home/pjreddie/backup/ +labels = data/9k.labels +names = data/9k.names +top=5 + diff --git a/image.darknet/inst/include/darknet/cfg/openimages.data b/image.darknet/inst/include/darknet/cfg/openimages.data new file mode 100644 index 0000000..fa80e5a --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/openimages.data @@ -0,0 +1,8 @@ +classes= 601 +train = /home/pjreddie/data/openimsv4/openimages.train.list +#valid = coco_testdev +valid = data/coco_val_5k.list +names = data/openimages.names +backup = /home/pjreddie/backup/ +eval=coco + diff --git a/image.darknet/inst/include/darknet/cfg/resnet101.cfg b/image.darknet/inst/include/darknet/cfg/resnet101.cfg new file mode 100644 index 0000000..de45882 --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/resnet101.cfg @@ -0,0 +1,990 @@ +[net] +# Training +# batch=128 +# subdivisions=2 + +# Testing +batch=1 +subdivisions=1 + +height=256 +width=256 +channels=3 +min_crop=128 +max_crop=448 + +burn_in=1000 +learning_rate=0.1 +policy=poly +power=4 +max_batches=800000 +momentum=0.9 +decay=0.0005 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 + + + +[convolutional] +batch_normalize=1 +filters=64 +size=7 +stride=2 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +# Conv 4 +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +#Conv 5 +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + + + + +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear + +[avgpool] + +[softmax] +groups=1 + +[cost] +type=sse + diff --git a/image.darknet/inst/include/darknet/cfg/msr_152.cfg b/image.darknet/inst/include/darknet/cfg/resnet152.cfg similarity index 92% rename from image.darknet/inst/include/darknet/cfg/msr_152.cfg rename to image.darknet/inst/include/darknet/cfg/resnet152.cfg index b19c999..e8e3297 100644 --- a/image.darknet/inst/include/darknet/cfg/msr_152.cfg +++ b/image.darknet/inst/include/darknet/cfg/resnet152.cfg @@ -1,26 +1,30 @@ [net] -batch=128 -subdivisions=8 +# Training +# batch=128 +# subdivisions=8 + +# Testing +batch=1 +subdivisions=1 + height=256 width=256 +max_crop=448 channels=3 momentum=0.9 -decay=0.0001 +decay=0.0005 +burn_in=1000 learning_rate=0.1 policy=poly power=4 -max_batches=500000 - -[crop] -crop_height=224 -crop_width=224 -flip=1 -saturation=1 -exposure=1 -angle=0 +max_batches=1600000 -##### Conv 1 ##### +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 [convolutional] batch_normalize=1 @@ -31,13 +35,9 @@ pad=1 activation=leaky [maxpool] -size=3 +size=2 stride=2 - -##### Conv 2_x ##### - - [convolutional] batch_normalize=1 filters=64 @@ -62,19 +62,8 @@ stride=1 pad=1 activation=linear -[route] -layers=-4 - -[convolutional] -batch_normalize=1 -size=1 -stride=1 -pad=1 -activation=linear -filters=256 - [shortcut] -from = -3 +from=-4 activation=leaky [convolutional] @@ -102,8 +91,7 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky [convolutional] @@ -131,13 +119,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - -##### Conv 3_x ##### - [convolutional] batch_normalize=1 filters=128 @@ -162,23 +146,10 @@ stride=1 pad=1 activation=linear - -[route] -layers=-4 - -[convolutional] -batch_normalize=1 -size=1 -stride=2 -pad=1 -activation=linear -filters=512 - [shortcut] -from = -3 +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=128 @@ -204,11 +175,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=128 @@ -234,11 +203,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=128 @@ -264,11 +231,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=128 @@ -294,11 +259,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=128 @@ -324,11 +287,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=128 @@ -354,11 +315,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=128 @@ -384,14 +343,11 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - -##### Conv 4_x ##### - +# Conv 4 [convolutional] batch_normalize=1 filters=256 @@ -416,23 +372,10 @@ stride=1 pad=1 activation=linear - -[route] -layers=-4 - -[convolutional] -batch_normalize=1 -size=1 -stride=2 -pad=1 -activation=linear -filters=1024 - [shortcut] -from = -3 +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -458,11 +401,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -488,11 +429,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -518,11 +457,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -548,11 +485,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -578,11 +513,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -608,11 +541,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -638,11 +569,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -668,11 +597,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -698,11 +625,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -728,11 +653,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -758,11 +681,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -788,11 +709,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -818,11 +737,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -848,11 +765,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -878,11 +793,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -908,11 +821,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -938,11 +849,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -968,11 +877,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -998,11 +905,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1028,11 +933,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1058,11 +961,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1088,11 +989,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1118,11 +1017,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1148,11 +1045,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1178,11 +1073,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1208,11 +1101,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1238,11 +1129,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1268,11 +1157,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1298,11 +1185,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1328,11 +1213,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1358,11 +1241,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1388,11 +1269,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1418,11 +1297,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1448,11 +1325,9 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=256 @@ -1478,13 +1353,10 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky - -##### Conv 5_x ##### - +#Conv 5 [convolutional] batch_normalize=1 filters=512 @@ -1509,23 +1381,10 @@ stride=1 pad=1 activation=linear - -[route] -layers=-4 - -[convolutional] -batch_normalize=1 -size=1 -stride=2 -pad=1 -activation=linear -filters=2048 - [shortcut] -from = -3 +from=-4 activation=leaky - [convolutional] batch_normalize=1 filters=512 @@ -1551,8 +1410,7 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky [convolutional] @@ -1580,19 +1438,23 @@ pad=1 activation=linear [shortcut] -from = -4 - +from=-4 activation=leaky -[avgpool] -[connected] -output=1000 -activation=leaky + + + + +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear + +[avgpool] [softmax] groups=1 -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/yolov1/yolo2.cfg b/image.darknet/inst/include/darknet/cfg/resnet18.cfg similarity index 58% rename from image.darknet/inst/include/darknet/cfg/yolov1/yolo2.cfg rename to image.darknet/inst/include/darknet/cfg/resnet18.cfg index b46a0d6..275f4bd 100644 --- a/image.darknet/inst/include/darknet/cfg/yolov1/yolo2.cfg +++ b/image.darknet/inst/include/darknet/cfg/resnet18.cfg @@ -1,17 +1,32 @@ [net] +# Training +# batch=128 +# subdivisions=1 + +# Testing batch=1 subdivisions=1 -height=448 -width=448 + +height=256 +width=256 channels=3 +min_crop=128 +max_crop=448 + +burn_in=1000 +learning_rate=0.1 +policy=poly +power=4 +max_batches=800000 momentum=0.9 decay=0.0005 -learning_rate=0.0005 -policy=steps -steps=200,400,600,20000,30000 -scales=2.5,2,2,.1,.1 -max_batches = 40000 +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 + [convolutional] batch_normalize=1 @@ -25,29 +40,32 @@ activation=leaky size=2 stride=2 + +# Residual Block [convolutional] batch_normalize=1 -filters=192 +filters=64 size=3 stride=1 pad=1 activation=leaky -[maxpool] -size=2 -stride=2 - [convolutional] batch_normalize=1 -filters=128 -size=1 +filters=64 +size=3 stride=1 pad=1 +activation=linear + +[shortcut] activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 -filters=256 +filters=64 size=3 stride=1 pad=1 @@ -55,51 +73,41 @@ activation=leaky [convolutional] batch_normalize=1 -filters=256 -size=1 +filters=64 +size=3 stride=1 pad=1 +activation=linear + +[shortcut] activation=leaky +from=-3 +# Strided Residual Block [convolutional] batch_normalize=1 -filters=512 +filters=128 size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 stride=2 - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 -filters=512 +filters=128 size=3 stride=1 pad=1 -activation=leaky +activation=linear -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 +[shortcut] activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 -filters=512 +filters=128 size=3 stride=1 pad=1 @@ -107,145 +115,114 @@ activation=leaky [convolutional] batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 +filters=128 size=3 stride=1 pad=1 +activation=linear + +[shortcut] activation=leaky +from=-3 + +# Strided Residual Block [convolutional] batch_normalize=1 filters=256 -size=1 -stride=1 +size=3 +stride=2 pad=1 activation=leaky [convolutional] batch_normalize=1 -filters=512 +filters=256 size=3 stride=1 pad=1 +activation=linear + +[shortcut] activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 -filters=512 -size=1 +filters=256 +size=3 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 -filters=1024 +filters=256 size=3 stride=1 pad=1 +activation=linear + +[shortcut] activation=leaky +from=-3 -[maxpool] -size=2 -stride=2 +# Strided Residual Block [convolutional] batch_normalize=1 filters=512 -size=1 -stride=1 +size=3 +stride=2 pad=1 activation=leaky [convolutional] batch_normalize=1 -filters=1024 +filters=512 size=3 stride=1 pad=1 +activation=linear + +[shortcut] activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=512 -size=1 +size=3 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 -filters=1024 +filters=512 size=3 stride=1 pad=1 +activation=linear + +[shortcut] activation=leaky +from=-3 -####### -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky -[convolutional] -batch_normalize=1 -size=3 -stride=2 -pad=1 -filters=1024 -activation=leaky -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky +[avgpool] [convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[local] -size=3 +filters=1000 +size=1 stride=1 pad=1 -filters=256 -activation=leaky - -[connected] -output= 1715 activation=linear -[detection] -classes=20 -coords=4 -rescore=1 -side=7 -num=3 -softmax=0 -sqrt=1 -jitter=.2 - -object_scale=1 -noobject_scale=.5 -class_scale=1 -coord_scale=5 +[softmax] +groups=1 diff --git a/image.darknet/inst/include/darknet/cfg/msr_34.cfg b/image.darknet/inst/include/darknet/cfg/resnet34.cfg similarity index 77% rename from image.darknet/inst/include/darknet/cfg/msr_34.cfg rename to image.darknet/inst/include/darknet/cfg/resnet34.cfg index 5ae23cf..9f68f09 100644 --- a/image.darknet/inst/include/darknet/cfg/msr_34.cfg +++ b/image.darknet/inst/include/darknet/cfg/resnet34.cfg @@ -1,24 +1,32 @@ [net] -batch=128 +# Training +# batch=128 +# subdivisions=2 + +# Testing +batch=1 subdivisions=1 + height=256 width=256 channels=3 -momentum=0.9 -decay=0.0005 +min_crop=128 +max_crop=448 +burn_in=1000 learning_rate=0.1 policy=poly power=4 -max_batches=500000 +max_batches=800000 +momentum=0.9 +decay=0.0005 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 -[crop] -crop_height=224 -crop_width=224 -flip=1 -saturation=1 -exposure=1 -angle=0 [convolutional] batch_normalize=1 @@ -29,9 +37,10 @@ pad=1 activation=leaky [maxpool] -size=3 +size=2 stride=2 +# Residual Block [convolutional] batch_normalize=1 filters=64 @@ -46,11 +55,13 @@ filters=64 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=64 @@ -65,11 +76,13 @@ filters=64 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=64 @@ -84,14 +97,13 @@ filters=64 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 - - - +activation=leaky +from=-3 +# Strided Residual Block [convolutional] batch_normalize=1 filters=128 @@ -106,11 +118,13 @@ filters=128 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=128 @@ -125,11 +139,13 @@ filters=128 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=128 @@ -144,11 +160,13 @@ filters=128 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=128 @@ -163,16 +181,13 @@ filters=128 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 - - - - - +activation=leaky +from=-3 +# Strided Residual Block [convolutional] batch_normalize=1 filters=256 @@ -187,11 +202,13 @@ filters=256 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=256 @@ -206,11 +223,13 @@ filters=256 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=256 @@ -225,11 +244,13 @@ filters=256 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=256 @@ -244,11 +265,13 @@ filters=256 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=256 @@ -263,11 +286,13 @@ filters=256 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=256 @@ -282,19 +307,13 @@ filters=256 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 - - - - - - - - +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=512 @@ -309,11 +328,13 @@ filters=512 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=512 @@ -328,11 +349,13 @@ filters=512 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 +# Residual Block [convolutional] batch_normalize=1 filters=512 @@ -347,20 +370,23 @@ filters=512 size=3 stride=1 pad=1 -activation=leaky +activation=linear [shortcut] -from = -3 +activation=leaky +from=-3 + + [avgpool] -[connected] -output=1000 -activation=leaky +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear [softmax] groups=1 -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/resnet50.cfg b/image.darknet/inst/include/darknet/cfg/resnet50.cfg new file mode 100644 index 0000000..d0d7c51 --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/resnet50.cfg @@ -0,0 +1,510 @@ +[net] +# Training +# batch=128 +# subdivisions=4 + +# Testing +batch=1 +subdivisions=1 + +height=256 +width=256 +channels=3 +min_crop=128 +max_crop=448 + +burn_in=1000 +learning_rate=0.1 +policy=poly +power=4 +max_batches=800000 +momentum=0.9 +decay=0.0005 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 + + +[convolutional] +batch_normalize=1 +filters=64 +size=7 +stride=2 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +# Conv 4 +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +#Conv 5 +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + + + +[avgpool] + +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear + +[softmax] +groups=1 + + diff --git a/image.darknet/inst/include/darknet/cfg/resnext101-32x4d.cfg b/image.darknet/inst/include/darknet/cfg/resnext101-32x4d.cfg new file mode 100644 index 0000000..8538ccc --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/resnext101-32x4d.cfg @@ -0,0 +1,1053 @@ +[net] +# Training +# batch=128 +# subdivisions=8 + +# Testing +batch=1 +subdivisions=1 + +height=256 +width=256 +channels=3 +min_crop=128 +max_crop=448 + +burn_in=1000 +learning_rate=0.1 +policy=poly +power=4 +max_batches=800000 +momentum=0.9 +decay=0.0005 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 + + + +[convolutional] +batch_normalize=1 +filters=64 +size=7 +stride=2 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=512 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=4096 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=4096 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=4096 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + + +[avgpool] + +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear + +[softmax] +groups=1 + diff --git a/image.darknet/inst/include/darknet/cfg/resnext152-32x4d.cfg b/image.darknet/inst/include/darknet/cfg/resnext152-32x4d.cfg new file mode 100644 index 0000000..48279fd --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/resnext152-32x4d.cfg @@ -0,0 +1,1562 @@ +[net] +# Training +# batch=128 +# subdivisions=16 + +# Testing +batch=1 +subdivisions=1 + +height=256 +width=256 +channels=3 +min_crop=128 +max_crop=448 + +burn_in=1000 +learning_rate=0.1 +policy=poly +power=4 +max_batches=800000 +momentum=0.9 +decay=0.0005 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 + + +[convolutional] +batch_normalize=1 +filters=64 +size=7 +stride=2 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=512 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=4096 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=4096 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +groups = 32 +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=4096 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + + + +[avgpool] + +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear + +[softmax] +groups=1 + diff --git a/image.darknet/inst/include/darknet/cfg/resnext50.cfg b/image.darknet/inst/include/darknet/cfg/resnext50.cfg new file mode 100644 index 0000000..12aebdf --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/resnext50.cfg @@ -0,0 +1,523 @@ +[net] +# Training +# batch=128 +# subdivisions=4 + +# Testing +batch=1 +subdivisions=1 + +height=256 +width=256 +channels=3 +min_crop=128 +max_crop=448 + +burn_in=1000 +learning_rate=0.1 +policy=poly +power=4 +max_batches=800000 +momentum=0.9 +decay=0.0005 + +angle=7 +hue=.1 +saturation=.75 +exposure=.75 +aspect=.75 + + + +[convolutional] +batch_normalize=1 +filters=64 +size=7 +stride=2 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +groups=32 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + + +# Conv 4 +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +groups=32 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +#Conv 5 +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +groups=32 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +groups=32 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=2048 +size=1 +stride=1 +pad=1 +activation=linear + +[shortcut] +from=-4 +activation=leaky + +[avgpool] + +[convolutional] +filters=1000 +size=1 +stride=1 +pad=1 +activation=linear + +[softmax] +groups=1 + + diff --git a/image.darknet/inst/include/darknet/cfg/rnn.cfg b/image.darknet/inst/include/darknet/cfg/rnn.cfg index 68c032d..61b202f 100644 --- a/image.darknet/inst/include/darknet/cfg/rnn.cfg +++ b/image.darknet/inst/include/darknet/cfg/rnn.cfg @@ -35,6 +35,4 @@ activation=leaky [softmax] -[cost] -type=sse diff --git a/image.darknet/inst/include/darknet/cfg/rnn.train.cfg b/image.darknet/inst/include/darknet/cfg/rnn.train.cfg index 9139757..b974899 100644 --- a/image.darknet/inst/include/darknet/cfg/rnn.train.cfg +++ b/image.darknet/inst/include/darknet/cfg/rnn.train.cfg @@ -35,6 +35,4 @@ activation=leaky [softmax] -[cost] -type=sse diff --git a/image.darknet/inst/include/darknet/cfg/strided.cfg b/image.darknet/inst/include/darknet/cfg/strided.cfg index a52700b..2f74508 100644 --- a/image.darknet/inst/include/darknet/cfg/strided.cfg +++ b/image.darknet/inst/include/darknet/cfg/strided.cfg @@ -180,6 +180,3 @@ activation=ramp [softmax] -[cost] -type=sse - diff --git a/image.darknet/inst/include/darknet/cfg/tiny.cfg b/image.darknet/inst/include/darknet/cfg/tiny.cfg index 99c2603..f97327c 100644 --- a/image.darknet/inst/include/darknet/cfg/tiny.cfg +++ b/image.darknet/inst/include/darknet/cfg/tiny.cfg @@ -1,6 +1,10 @@ [net] +# Train batch=128 subdivisions=1 +# Test +# batch=1 +# subdivisions=1 height=224 width=224 channels=3 @@ -167,6 +171,4 @@ activation=linear [softmax] groups=1 -[cost] -type=sse diff --git a/image.darknet/inst/include/darknet/cfg/vgg-16.cfg b/image.darknet/inst/include/darknet/cfg/vgg-16.cfg index 2b6f702..c73b17b 100644 --- a/image.darknet/inst/include/darknet/cfg/vgg-16.cfg +++ b/image.darknet/inst/include/darknet/cfg/vgg-16.cfg @@ -1,6 +1,12 @@ [net] -batch=128 -subdivisions=4 +# Training +# batch=128 +# subdivisions=4 + +# Testing +batch=1 +subdivisions=1 + height=256 width=256 channels=3 @@ -148,6 +154,4 @@ activation=linear [softmax] groups=1 -[cost] -type=sse diff --git a/image.darknet/inst/include/darknet/cfg/yolo9000.cfg b/image.darknet/inst/include/darknet/cfg/yolo9000.cfg index 981491d..e745f78 100644 --- a/image.darknet/inst/include/darknet/cfg/yolo9000.cfg +++ b/image.darknet/inst/include/darknet/cfg/yolo9000.cfg @@ -1,17 +1,24 @@ [net] +# Testing batch=1 subdivisions=1 -height=416 -width=416 +# Training +# batch=64 +# subdivisions=8 +batch=1 +subdivisions=1 +height=544 +width=544 channels=3 momentum=0.9 decay=0.0005 -learning_rate=0.00001 -max_batches = 242200 +learning_rate=0.001 +burn_in=1000 +max_batches = 500200 policy=steps -steps=500,200000,240000 -scales=10,.1,.1 +steps=400000,450000 +scales=.1,.1 hue=.1 saturation=.75 diff --git a/image.darknet/inst/include/darknet/cfg/yolov1/tiny-yolo.cfg b/image.darknet/inst/include/darknet/cfg/yolov1-tiny.cfg similarity index 94% rename from image.darknet/inst/include/darknet/cfg/yolov1/tiny-yolo.cfg rename to image.darknet/inst/include/darknet/cfg/yolov1-tiny.cfg index ac4b346..a5e7b49 100644 --- a/image.darknet/inst/include/darknet/cfg/yolov1/tiny-yolo.cfg +++ b/image.darknet/inst/include/darknet/cfg/yolov1-tiny.cfg @@ -1,6 +1,10 @@ [net] -batch=64 -subdivisions=2 +# Testing +batch=1 +subdivisions=1 +# Training +# batch=64 +# subdivisions=8 height=448 width=448 channels=3 diff --git a/image.darknet/inst/include/darknet/cfg/yolov1/yolo.cfg b/image.darknet/inst/include/darknet/cfg/yolov1.cfg similarity index 98% rename from image.darknet/inst/include/darknet/cfg/yolov1/yolo.cfg rename to image.darknet/inst/include/darknet/cfg/yolov1.cfg index c4f415c..06cf6e6 100644 --- a/image.darknet/inst/include/darknet/cfg/yolov1/yolo.cfg +++ b/image.darknet/inst/include/darknet/cfg/yolov1.cfg @@ -1,6 +1,10 @@ [net] +# Testing batch=1 subdivisions=1 +# Training +# batch=64 +# subdivisions=8 height=448 width=448 channels=3 diff --git a/image.darknet/inst/include/darknet/cfg/yolov1/tiny-coco.cfg b/image.darknet/inst/include/darknet/cfg/yolov1/tiny-coco.cfg deleted file mode 100644 index e58c73a..0000000 --- a/image.darknet/inst/include/darknet/cfg/yolov1/tiny-coco.cfg +++ /dev/null @@ -1,125 +0,0 @@ -[net] -batch=64 -subdivisions=2 -height=448 -width=448 -channels=3 -momentum=0.9 -decay=0.0005 - -hue = .1 -saturation=.75 -exposure=.75 - -learning_rate=0.0005 -policy=steps -steps=200,400,600,800,100000,150000 -scales=2.5,2,2,2,.1,.1 -max_batches = 200000 - -[convolutional] -batch_normalize=1 -filters=16 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=32 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=64 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=128 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=256 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=256 -activation=leaky - -[connected] -output= 4655 -activation=linear - -[detection] -classes=80 -coords=4 -rescore=1 -side=7 -num=3 -softmax=0 -sqrt=1 -jitter=.2 - -object_scale=1 -noobject_scale=.5 -class_scale=1 -coord_scale=5 diff --git a/image.darknet/inst/include/darknet/cfg/yolov1/xyolo.test.cfg b/image.darknet/inst/include/darknet/cfg/yolov1/xyolo.test.cfg deleted file mode 100644 index 5f3e6f4..0000000 --- a/image.darknet/inst/include/darknet/cfg/yolov1/xyolo.test.cfg +++ /dev/null @@ -1,143 +0,0 @@ -[net] -batch=1 -subdivisions=1 -height=448 -width=448 -channels=3 -momentum=0.9 -decay=0.0005 - -learning_rate=0.0001 -policy=steps -steps=20,40,60,80,20000,30000 -scales=5,5,2,2,.1,.1 -max_batches = 40000 - -[convolutional] -batch_normalize=1 -filters=16 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[batchnorm] - -[convolutional] -xnor = 1 -batch_normalize=1 -filters=32 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[batchnorm] - -[convolutional] -xnor = 1 -batch_normalize=1 -filters=64 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[batchnorm] - -[convolutional] -xnor = 1 -batch_normalize=1 -filters=128 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[batchnorm] - -[convolutional] -xnor = 1 -batch_normalize=1 -filters=256 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[batchnorm] - -[convolutional] -xnor = 1 -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[batchnorm] - -[convolutional] -xnor = 1 -batch_normalize=1 -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -[batchnorm] - -[convolutional] -xnor = 1 -batch_normalize=1 -filters=256 -size=3 -stride=1 -pad=1 -activation=leaky - -[connected] -output= 1470 -activation=linear - -[detection] -classes=20 -coords=4 -rescore=1 -side=7 -num=2 -softmax=0 -sqrt=1 -jitter=.2 - -object_scale=1 -noobject_scale=.5 -class_scale=1 -coord_scale=5 - diff --git a/image.darknet/inst/include/darknet/cfg/yolov1/yolo-coco.cfg b/image.darknet/inst/include/darknet/cfg/yolov1/yolo-coco.cfg deleted file mode 100644 index ed3f2d6..0000000 --- a/image.darknet/inst/include/darknet/cfg/yolov1/yolo-coco.cfg +++ /dev/null @@ -1,255 +0,0 @@ -[net] -batch=64 -subdivisions=4 -height=448 -width=448 -channels=3 -momentum=0.9 -decay=0.0005 - -hue = .1 -saturation=.75 -exposure=.75 - -learning_rate=0.0005 -policy=steps -steps=200,400,600,800,100000,150000 -scales=2.5,2,2,2,.1,.1 -max_batches = 200000 - -[convolutional] -batch_normalize=1 -filters=64 -size=7 -stride=2 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=192 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=128 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=512 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -####### - -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -batch_normalize=1 -size=3 -stride=2 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[local] -size=3 -stride=1 -pad=1 -filters=256 -activation=leaky - -[connected] -output= 4655 -activation=linear - -[detection] -classes=80 -coords=4 -rescore=1 -side=7 -num=3 -softmax=0 -sqrt=1 -jitter=.2 - -object_scale=1 -noobject_scale=.5 -class_scale=1 -coord_scale=5 - diff --git a/image.darknet/inst/include/darknet/cfg/yolov1/yolo-small.cfg b/image.darknet/inst/include/darknet/cfg/yolov1/yolo-small.cfg deleted file mode 100644 index 2a84485..0000000 --- a/image.darknet/inst/include/darknet/cfg/yolov1/yolo-small.cfg +++ /dev/null @@ -1,239 +0,0 @@ -[net] -batch=64 -subdivisions=64 -height=448 -width=448 -channels=3 -momentum=0.9 -decay=0.0005 - -learning_rate=0.001 -policy=steps -steps=200,400,600,20000,30000 -scales=2.5,2,2,.1,.1 -max_batches = 40000 - -[crop] -crop_width=448 -crop_height=448 -flip=0 -angle=0 -saturation = 1.5 -exposure = 1.5 - -[convolutional] -filters=64 -size=7 -stride=2 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -filters=192 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -filters=128 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=256 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=512 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -filters=512 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=512 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -####### - -[convolutional] -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -size=3 -stride=2 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[connected] -output=512 -activation=leaky - -[connected] -output=4096 -activation=leaky - -[dropout] -probability=.5 - -[connected] -output= 1470 -activation=linear - -[detection] -classes=20 -coords=4 -rescore=1 -side=7 -num=2 -softmax=0 -sqrt=1 -jitter=.2 - -object_scale=1 -noobject_scale=.5 -class_scale=1 -coord_scale=5 - diff --git a/image.darknet/inst/include/darknet/cfg/yolov1/yolo.train.cfg b/image.darknet/inst/include/darknet/cfg/yolov1/yolo.train.cfg deleted file mode 100644 index 01aeb5e..0000000 --- a/image.darknet/inst/include/darknet/cfg/yolov1/yolo.train.cfg +++ /dev/null @@ -1,257 +0,0 @@ -[net] -batch=64 -subdivisions=4 -height=448 -width=448 -channels=3 -momentum=0.9 -decay=0.0005 -saturation=1.5 -exposure=1.5 -hue=.1 - -learning_rate=0.0005 -policy=steps -steps=200,400,600,20000,30000 -scales=2.5,2,2,.1,.1 -max_batches = 40000 - -[convolutional] -batch_normalize=1 -filters=64 -size=7 -stride=2 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=192 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=128 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=256 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -[maxpool] -size=2 -stride=2 - -[convolutional] -batch_normalize=1 -filters=512 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=512 -size=1 -stride=1 -pad=1 -activation=leaky - -[convolutional] -batch_normalize=1 -filters=1024 -size=3 -stride=1 -pad=1 -activation=leaky - -####### - -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -batch_normalize=1 -size=3 -stride=2 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[convolutional] -batch_normalize=1 -size=3 -stride=1 -pad=1 -filters=1024 -activation=leaky - -[local] -size=3 -stride=1 -pad=1 -filters=256 -activation=leaky - -[dropout] -probability=.5 - -[connected] -output= 1715 -activation=linear - -[detection] -classes=20 -coords=4 -rescore=1 -side=7 -num=3 -softmax=0 -sqrt=1 -jitter=.2 - -object_scale=1 -noobject_scale=.5 -class_scale=1 -coord_scale=5 - diff --git a/image.darknet/inst/include/darknet/cfg/tiny-yolo-voc.cfg b/image.darknet/inst/include/darknet/cfg/yolov2-tiny-voc.cfg similarity index 93% rename from image.darknet/inst/include/darknet/cfg/tiny-yolo-voc.cfg rename to image.darknet/inst/include/darknet/cfg/yolov2-tiny-voc.cfg index 1f33c35..c4c127c 100644 --- a/image.darknet/inst/include/darknet/cfg/tiny-yolo-voc.cfg +++ b/image.darknet/inst/include/darknet/cfg/yolov2-tiny-voc.cfg @@ -1,6 +1,10 @@ [net] -batch=64 -subdivisions=8 +# Testing +batch=1 +subdivisions=1 +# Training +# batch=64 +# subdivisions=2 width=416 height=416 channels=3 @@ -12,7 +16,7 @@ exposure = 1.5 hue=.1 learning_rate=0.001 -max_batches = 40100 +max_batches = 40200 policy=steps steps=-1,100,20000,30000 scales=.1,10,.1,.1 diff --git a/image.darknet/inst/include/darknet/cfg/tiny-yolo.cfg b/image.darknet/inst/include/darknet/cfg/yolov2-tiny.cfg similarity index 82% rename from image.darknet/inst/include/darknet/cfg/tiny-yolo.cfg rename to image.darknet/inst/include/darknet/cfg/yolov2-tiny.cfg index 5580098..81d0ac4 100644 --- a/image.darknet/inst/include/darknet/cfg/tiny-yolo.cfg +++ b/image.darknet/inst/include/darknet/cfg/yolov2-tiny.cfg @@ -1,6 +1,10 @@ [net] -batch=64 -subdivisions=8 +# Testing +batch=1 +subdivisions=1 +# Training +# batch=64 +# subdivisions=2 width=416 height=416 channels=3 @@ -12,10 +16,11 @@ exposure = 1.5 hue=.1 learning_rate=0.001 -max_batches = 120000 +burn_in=1000 +max_batches = 500200 policy=steps -steps=-1,100,80000,100000 -scales=.1,10,.1,.1 +steps=400000,450000 +scales=.1,.1 [convolutional] batch_normalize=1 @@ -104,7 +109,7 @@ batch_normalize=1 size=3 stride=1 pad=1 -filters=1024 +filters=512 activation=leaky [convolutional] @@ -115,14 +120,14 @@ filters=425 activation=linear [region] -anchors = 0.738768,0.874946, 2.42204,2.65704, 4.30971,7.04493, 10.246,4.59428, 12.6868,11.8741 +anchors = 0.57273, 0.677385, 1.87446, 2.06253, 3.33843, 5.47434, 7.88282, 3.52778, 9.77052, 9.16828 bias_match=1 classes=80 coords=4 num=5 softmax=1 jitter=.2 -rescore=1 +rescore=0 object_scale=5 noobject_scale=1 diff --git a/image.darknet/inst/include/darknet/cfg/yolo.cfg b/image.darknet/inst/include/darknet/cfg/yolov2-voc.cfg similarity index 87% rename from image.darknet/inst/include/darknet/cfg/yolo.cfg rename to image.darknet/inst/include/darknet/cfg/yolov2-voc.cfg index fda339a..dbf2de2 100644 --- a/image.darknet/inst/include/darknet/cfg/yolo.cfg +++ b/image.darknet/inst/include/darknet/cfg/yolov2-voc.cfg @@ -1,8 +1,12 @@ [net] +# Testing batch=1 subdivisions=1 -width=416 +# Training +# batch=64 +# subdivisions=8 height=416 +width=416 channels=3 momentum=0.9 decay=0.0005 @@ -12,10 +16,11 @@ exposure = 1.5 hue=.1 learning_rate=0.001 -max_batches = 120000 +burn_in=1000 +max_batches = 80200 policy=steps -steps=-1,100,80000,100000 -scales=.1,10,.1,.1 +steps=40000,60000 +scales=.1,.1 [convolutional] batch_normalize=1 @@ -203,11 +208,19 @@ activation=leaky [route] layers=-9 +[convolutional] +batch_normalize=1 +size=1 +stride=1 +pad=1 +filters=64 +activation=leaky + [reorg] stride=2 [route] -layers=-1,-3 +layers=-1,-4 [convolutional] batch_normalize=1 @@ -221,17 +234,18 @@ activation=leaky size=1 stride=1 pad=1 -filters=425 +filters=125 activation=linear + [region] -anchors = 0.738768,0.874946, 2.42204,2.65704, 4.30971,7.04493, 10.246,4.59428, 12.6868,11.8741 +anchors = 1.3221, 1.73145, 3.19275, 4.00944, 5.05587, 8.09892, 9.47112, 4.84053, 11.2364, 10.0071 bias_match=1 -classes=80 +classes=20 coords=4 num=5 softmax=1 -jitter=.2 +jitter=.3 rescore=1 object_scale=5 @@ -241,4 +255,4 @@ coord_scale=1 absolute=1 thresh = .6 -random=0 +random=1 diff --git a/image.darknet/inst/include/darknet/cfg/yolov3-openimages.cfg b/image.darknet/inst/include/darknet/cfg/yolov3-openimages.cfg new file mode 100644 index 0000000..65d241a --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/yolov3-openimages.cfg @@ -0,0 +1,789 @@ +[net] +# Testing + batch=1 + subdivisions=1 +# Training +batch=64 +subdivisions=16 +width=608 +height=608 +channels=3 +momentum=0.9 +decay=0.0005 +angle=0 +saturation = 1.5 +exposure = 1.5 +hue=.1 + +learning_rate=0.001 +burn_in=5000 +max_batches = 500200 +policy=steps +steps=400000,450000 +scales=.1,.1 + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +# Downsample + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +###################### + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=1818 +activation=linear + + +[yolo] +mask = 6,7,8 +anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 +classes=601 +num=9 +jitter=.3 +ignore_thresh = .7 +truth_thresh = 1 +random=1 + + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 61 + + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=1818 +activation=linear + + +[yolo] +mask = 3,4,5 +anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 +classes=601 +num=9 +jitter=.3 +ignore_thresh = .7 +truth_thresh = 1 +random=1 + + + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 36 + + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=1818 +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=601 +num=9 +jitter=.3 +ignore_thresh = .7 +truth_thresh = 1 +random=1 + diff --git a/image.darknet/inst/include/darknet/cfg/yolov3-spp.cfg b/image.darknet/inst/include/darknet/cfg/yolov3-spp.cfg new file mode 100644 index 0000000..4ad2a05 --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/yolov3-spp.cfg @@ -0,0 +1,822 @@ +[net] +# Testing +batch=1 +subdivisions=1 +# Training +# batch=64 +# subdivisions=16 +width=608 +height=608 +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 = 500200 +policy=steps +steps=400000,450000 +scales=.1,.1 + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +# Downsample + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +###################### + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +### SPP ### +[maxpool] +stride=1 +size=5 + +[route] +layers=-2 + +[maxpool] +stride=1 +size=9 + +[route] +layers=-4 + +[maxpool] +stride=1 +size=13 + +[route] +layers=-1,-3,-5,-6 + +### End SPP ### + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=255 +activation=linear + + +[yolo] +mask = 6,7,8 +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 + + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 61 + + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=255 +activation=linear + + +[yolo] +mask = 3,4,5 +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 + + + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 36 + + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=255 +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=80 +num=9 +jitter=.3 +ignore_thresh = .7 +truth_thresh = 1 +random=1 + diff --git a/image.darknet/inst/include/darknet/cfg/yolov3-tiny.cfg b/image.darknet/inst/include/darknet/cfg/yolov3-tiny.cfg new file mode 100644 index 0000000..cfca3cf --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/yolov3-tiny.cfg @@ -0,0 +1,182 @@ +[net] +# Testing +batch=1 +subdivisions=1 +# Training +# batch=64 +# subdivisions=2 +width=416 +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 = 500200 +policy=steps +steps=400000,450000 +scales=.1,.1 + +[convolutional] +batch_normalize=1 +filters=16 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=2 + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[maxpool] +size=2 +stride=1 + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +########### + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=255 +activation=linear + + + +[yolo] +mask = 3,4,5 +anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319 +classes=80 +num=6 +jitter=.3 +ignore_thresh = .7 +truth_thresh = 1 +random=1 + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 8 + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=255 +activation=linear + +[yolo] +mask = 0,1,2 +anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319 +classes=80 +num=6 +jitter=.3 +ignore_thresh = .7 +truth_thresh = 1 +random=1 diff --git a/image.darknet/inst/include/darknet/cfg/yolov3-voc.cfg b/image.darknet/inst/include/darknet/cfg/yolov3-voc.cfg new file mode 100644 index 0000000..3f3e8df --- /dev/null +++ b/image.darknet/inst/include/darknet/cfg/yolov3-voc.cfg @@ -0,0 +1,785 @@ +[net] +# Testing + batch=1 + subdivisions=1 +# Training +# batch=64 +# subdivisions=16 +width=416 +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 +scales=.1,.1 + + + +[convolutional] +batch_normalize=1 +filters=32 +size=3 +stride=1 +pad=1 +activation=leaky + +# Downsample + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=32 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=64 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +# Downsample + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=2 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=1024 +size=3 +stride=1 +pad=1 +activation=leaky + +[shortcut] +from=-3 +activation=linear + +###################### + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=512 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=1024 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=75 +activation=linear + +[yolo] +mask = 6,7,8 +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 + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 61 + + + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=256 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=512 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=75 +activation=linear + +[yolo] +mask = 3,4,5 +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 + +[route] +layers = -4 + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[upsample] +stride=2 + +[route] +layers = -1, 36 + + + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +batch_normalize=1 +filters=128 +size=1 +stride=1 +pad=1 +activation=leaky + +[convolutional] +batch_normalize=1 +size=3 +stride=1 +pad=1 +filters=256 +activation=leaky + +[convolutional] +size=1 +stride=1 +pad=1 +filters=75 +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 + diff --git a/image.darknet/inst/include/darknet/data/kite.jpg b/image.darknet/inst/include/darknet/data/kite.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9eb325ac5fc375cb2513380087dd713be9be19d8 GIT binary patch literal 1415684 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

CHL3}D(gNVe0gJOTk>?s(;?qvU7O_lxgS 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ȟAW?yM-S}DgPA|A7?7EGlt{;7~wbv_XM z3q}E@AZCJ0ah|>!$OE}G$3@VTqeuKBdcQ$`gUV2!gohIbNb%aC0vh}nEtCc~*eIHp zl{KEE2kThZA-tKX_PxhrK8r9mJN%9{tqIym)eVt|+kn4&WK4lGR-E#0z}F6pn7$n{E8%LEMpRZkeGWgR zQv;+>-+sB3u(sNu+Ux_&oz}5`i?@h=304=egD9xORpM4z=-~NBQweS^o;V}xEmXnt z7vybz^bFP9g$Jv0_N|?5ASmyBoRT9OReEL#ah%C|Zn#0>2nj~*70ctB6u%8WYk=PO z()Ra+aPX`9fO6pg`mjDP_`+QW1$8;QkR+a=lr1|-BW!E_K$%TxWZr4iumsjwYhme^s4dvMacRXeux%IA+RwC6L0 z0;46usn=$n40<9Z5xz;LmA)gLb4+Z>Q}6NCxR}rLQr}0(i2?WDE>381kGk@wH!7SC z9BLZ#1k5m7>kZkj=|||A`-UFMUhY-X`f$8?*e5NmE=)(64!Q)nk%up7=7d}P3MX?S zoL_KznU2BwyCwXFeO0(7#7S2K*#2J;#7#FJTR+oe0Wq`_IjLXHEMkXM);B3u<2b)xJ%cOu$On zU8}VBt2t6|L;YawC$-MX)aCwuTZ_TxQjrPG4HUPL1}bKr6kqLqEqOAr^}_BlT+0zu zVg@)z?hGTAsMt3of_{!K5TeqOyw;~=%oM#5o!|ng_X=-TR!tzu&R4NaQ7v-?v-!=| zzea1e@A$2w3^!xyR$ib3a!`+|hGy9xS8j`ctE_0R}*N zloS|8;1Gd*+BrWI4yN7{rxXS_oAiqXA62R-42DO;N^3BLuib)CqCsPD03<};d?mC%aoGY*zaiT#hv2wyiB-e;4HUiw!lHOG?p9AW`{h+@Zq@bH9=TDjy7gx#S+W zjq(ZyfQq`$d=CVia+sV2*>_&ougM#&dzEh8Jd6LYF}NB6^SBPQ-2Tp=Pp>a|)3_@I zW*OI7EIU1~Cf^fLa5yuk`9c)xBJP9@nyB95VudaYxcmE2>`9Np``QjOQpsmkC>bXO z?`&K;bDhcy|Md14qY<_`jG!7M8lRc$SiJtRUo-$wr9`-|hEzYFh{CKcGOK=JZuVRq zps+&WKqYI-sz3hV*`xi_$DhdU?#i>(E9lfK&Yvqa1*-$w?|Li z$>;IoJ>Bd8soa=1fr_GZ1*@xdKWIqB3&`m5qSq78`_?(}oL{lGm7ZKXRS1^!KXbt& zwE*fQmZfkt3EqRAJBsoHhAOr-yNL%qv>HB`mKm#YFax^=b0}wv16rm3l?hVYJB zEDcaCzX^YycowtXg-*+L{qjEP8qLN@{Qi(gj)b>q^1J_cJKcDt#hxic7OZTQuz8YmD7bNg1wpC?2V!Vt9*HfM-~sCexs@au-g zXIqq-(ZyBnZpTb;`0=JO6iEo74$vEgZE@kh2nG&pb%hMs<^N;pTO66*|Nl4lJBi{T zghOtjj!W)!ay=or6+N^ie(yft z-#=h3FR$10`MB1eM)fT^$`eQ)in4Pf1cMbF59gwmANveE#5%xIkL4Bpv+W29%)z9k z+xE;j8`VnO{a|9cu&@OCyGHZ1yJIApUK8N`Yrpj$WRD*mD@}+CVrJcViRoU-k$WU0 z_g&8Tcf(M?>UZPif=ILrUJq|+GI6h#=bynPRaTVUDPjKeH#pS_ z)q1hl!YC%y+ur1Q)U_tnE)lG`!%bDd+;jiOJ40l#xs86D%>{7Tkb=1mN!u8|wae}P z30n#J2VUY8qyPSXV5}{w>BM7+bH?6r0vX3R<6enWbmW6jYCsWMYaS*abX{Rk5I%?R zdtasD!jXhYLLU3dxh0R!uQ?qitg=}hVY^c!u74r)T~!lXvOSo?xg8+r3q>u3W>ig> zHsOngjz2LnnOZe5b@+VVNEo7^qg>b|76hujsB8$JU|n$^vm%UqV?nR~ebLJ4WU}aQ z$T9x#4~sfR=?qo>dU}>_Q{+C!94i3g)|bm^@qzx&^se{G8B*~aaGbSr&}GD|p3St* z?AKYnn*+l=auS}3P`>N7A2F25$x2lGTEy74D=YV%Vlb$b7o}MCuE$RiKzfBhX&(ZG z1>Ho0-Q2_K4yaQVDaE5ZY9-4(goBxXBKR`pKPnpAh|C!djNHwxVYZ-O1=MWsCheXK zBVLWA0y4%i==Kb(P^MQL$nlTLJ)6@o#QhqsuwkXT~5<=uH_9stqujH*C>R| z0ut`&?eBg`ld)){=xJwP%&vbOt3&qV!zM(DXNX82A3wn;&*KElK)#WUAMO`FSsM4A z>IpzH`!NYS`xx~TftvE#$4!+#Qcv5qSJ_k^(_MU`rJb50W#uii)f5h4HIr+?5&wq8 zoHrQG(k7>4G){jxCVz^c7#K+$1jCyzM}O4;xYy||YJ3?9#X)&h{HF+F znVkdPYBV*o!OQZ5f%zpqyzYj7Y;rjFu9k)z}-Q|`5~>Qpgo{_SuQ?W zqd+;O&|o0n)2Piv!UvtduU#^3DDcj8b$OoH%5ER;nv@N}px<{=BTIf<75xw><4yW} z((J}N;ZdHqBo{ju!BGRCSQqAD!y4LCErITI5L_zXhN()Xn`tHrac*f25+!##6Cj ze3p1QU4;8yXo*>1o9VN&HUW*qZ%39LkPW5Y3GzSAo{``^?d9gjOXNiJg!1LtRz`JP z>U!S9MekB$y1hdN|F9CUC3(|GMs{;3zmy|C&gZ8K_Yf;pl_IcSoIU(6Wq9?!g%;N< zb`u^By-{Ye4X9eCpdc00+*{LL{&tO+amO%#`1Y&@)U~YsUQC^q_6$sDZl*_VnO3~8 z(FVFpI9*;c#s710=VbZU-5IFxtH`z0_fc7wcYid$40!Fl(J90bdJqZx&mKR z&NHH-mabLc*C8k3>#Zr)m}}Esu)V&3kzsmR=6+z^n6Zqu}YfiI`p{F&<5rH2kl`Gf-sk?vPTD!^Xm_!*D} z_WOchz5K(a!RUWdgb(8Xi0;>BaG=2G$k+|tjb}Q^oGeuZ@rOos#?g#&JMYfQ@T+BH zl=Vnl!ksO`G#{Kj#4(-A9}7|PMt*5kHiC}%U(J~)O%WD+v;IZGC}P#u6@sfj@dRRq zriY`n1XYggV%pdH*dX$RoS5JhWMJU2-`o&)#w5x7YSJ`&^T3kf7XXx_5;L%3-nx=e z89kibVRr43ycwj&2&!k&LH!I&A6;JGUW%h;qlJG6a~_%Acuflq1WDK|K-HH2_tges zW2eZRaG)%kL~ecLa)GySW-+ekpSxcY2hu?UE-+!sTY7674bNwN(M8NX5;3g%z2k9s z^iW2en*5+nskAUB{b%_(ZfDRkkMU{@13lc_(aN1ze{b_x7MpV~sQ3;j+DJ&XG0QsE zIY?8E=*3qZ`S&5bYyTFt#N@lR8eH-EhdEk$4H{I|nLeP2Ij)%RsG}3Pmwb9NMK2E8 zGI5FA>;iD(n*l|ISuOXq%v3hc6hr5Z;1q-fa}2u91xufL_E#tlNFTNa1|#dvc;yn! zW%C>>cVVKEB)_9#BN;r=M=1ck)dDz8s_Ps4iXV^p#-ky34!yQoAIhO*LQR7J;H-_P zsau`O`kL0ybmpPM@vdvzK(I4Tp;Lm1a;4h=*1i0iaV4bGj=I&!Q>)d)AQ%couy0)@4!J~(uSz$dz>`$)#-ob19dnYWW{?xYO}mLCZ4gI~8oHFvo?{x4#F zTvQF2<0(K15d;PET&d(7m|V!i{RMGyx;_(UJ+>FH;aRUPEgPNYA|N2BE`|hup}RQV zFsRHVnpW29y=sPFxKv8Wrc!6R)#c4V?kuTS*F?gUb|fFC^dIQHn<>|M?-0GVD;XvL z6Nq+L%BDs0|e~@1flk94=wGUr*Sp3p>&?Yh=kJM?aPy? zmCy8i$Jd6ar<=MH;vr`qwS5hZ!(5z68hN}Dq4lA}fe7+@m zlf_S*t;@P7)Uk$Qle19W8GD5ha1n8TNyH}|!7r!?o;c(kqf`Uy&SA>E6$MjrOhplk z>iy<`6;a&SppZUdYmr5O4&j>4^aC}i`2|Syd48U*!+-~4TbOc$>Rs$@gDdITZ4$D% z8*JM_b*1*psN}m-sQ@fB5!v(Bu*^e7a|HSclMX4AvKyCRW*389DD5EWVVjQa!ww*- zAl{PCeDvFS8W)}Y-h+2C$Hx3_>bvd?9ip4_i-x+N02#tg`j$V00Ey^;?7qEjZe>dR(L~u_)9PpD@mPY$jNu0@Ur}3(~^Qs8mPK{Gd&v0_} zy&ALD1?Xhr_UhgKb+a4a_h=vEJ0bB+Kp1oaOdP6c6BM;I#8H5`aT>e*rmyNoLFbCV zg5_#ZI6fg2$)I|u@aq$~4^ zBLnZ+6nK+%Hc5w&WH`IKv#t4qm%1TIKCh`xOCtw}l!_2d=SdfKm+-Wygdh^&Ax#ZZ?yVuf9^JmnQ)Z>&@#clprk`l<@-Xx^p(+xQ30OsrYvzhkm){1Yo{7 zfGhvs98+_am0oR#s*bj~jLZOTX9Iaqvr$nXkcEEjZX7wlb&}it&F9TcTa-6l%qI}l z395EIo$5eXfdO_x8JRK?B%&jRbm)b3z2bh#7$Z1;ot>6yW)TePO^~S16cMl=!5bcg zADmuGSikiRb?wJ^Q!AcxdI0;8$%(1TylispW{=x|X|w>mj8M>ek7NtVPp@vaHHn`$ zp01{DZ!&0q6;?ufZ}kS}?-mGx(=kF9P|;RyJ|WLaZs}cH2bOQ+l$y6aUG{8kgQG_Ih4kKAe2Fg<3~SZEF3~>8 zfv|V})O?dxZhZr5<-n3f>A$O@QYmld7Xu-;i^&0mmbz430dc?Q`)QXa3Ag8;Y<8qf zww%x7%{kwaDj;a?A|Qi`HWD5Ss6Z=e0R=F7haFjCRb0GivUN@^eCgwxFEboM4?hNF zuVSE9hBqVQP|?Pzl({RI4s{H-J0N@O!?JlHYQS_rMPMb%(kGbbo_`nO#3f$xWWD$3 zkAqmu^b^RoD#_$%>VsSRRq*4?IxU6hKDrH*>>{b6^X1a|)N@Ge>L#{^vu=?%Dl5X{ zO*hqyQVAl94?Mj)Cf6g7;S%%Q(YL#f{_4r*+U{*XQ)GLAQS`eR;2mNDHfX_lb77U> z4e;wS)~tE5a#lA!yDIV~V|;H9cnnUSkM_}b6b$s5bG$sv6A%9KmOokmyf9(dz4IGQ z@eOC~w+E6&#S&1Dfd{LIf?ox=;m= z2VP`C0eorGc=2O?dfb)ti@Z<1*2O^A@WrFlQ%V1UZjTS0OB~K+t|-P{?omtU!EUfC zPnhsw5h-hIAXmo;b-m3z%YMWtAxN{~{DyDDx%l6PoqBx3e)f-+)~Qvi7l*iTPVHcD zej4Y4$J?N^emi^hh2U74><^g87vj`f=)tugBK=!I`44pOc~@uIKClPoF5VmNJYZ&MHu44DO*geb9?9&#J2PI`?nSLx zJw43Acf^x@W4M5uv{y{;WX>~rj>B9p)pSKWA;-l)*748UOIE>*q?VNDoWH2sj|D|D z$jT<4Gu6{ZC4|(oU&k+*Y05nB2nV9)S)T1-R@1A~SdRHxSLVEg=RHk?vO!=xRx!V5 z1!i&Lb-|`}8F2Qy1FQ}&ms5UkAz8q0ST#OKF2Z!hRKDMK0lB>xr&cQdG4=N9AuuOot^pJ%;@1s=0f+lMu#%o{ybA843&wwB6|mdK9h+hCmvK zxVF)vdIl1e+WoV0iwinj@!NDxQg3U0?3;tbgDZzvuk*qljh8niu0ECADT1@DB3AH? zOWG*xb0}ut8o%ZLcuIMAbCa{W2CPX(BSsO@0xk}MKLHC9?4IHhFJChYP>j`Q{))tU z?E!vK)>l!SUfvm|fyYr+4^VSr=Rg2NtWK8##+U)I<@?_cBAm+&Qw;#s_2!otkl3zv z0^90#QSf>Zz;IcWStY%1S=L?O?qa04qOzGSfGM|gJxfJ8juS^DjH&ysLxp!+U1KXcj3K@%!#hd znRD9$UY->Ir}c)z*AO)eXlfF)&MQ3H$S$hl3!g;l_-$H`pJ=rJvO? zRM+;dttPG|^m_M=pBbgdoHpLdQR@ox4kWnaMPgm##8!M_`TqJV#;;gvS4UXg#6>Jr zLkz_r@R{kMUc$^+I7@+q6}Vvz?7dk8tD&sKnX-fH-e27nJIHb8*Gp}R4wv>j?p1!VBrc>#MjR}nz- zhZLhJ#xT;2z@~p$)L$DX54)bWE{Hy*bFdl!)!jSoOs#>;d|43;ThWZ-EzNnIV`Sja zGf&uLb3c|fv|_gb4?sGZwC8KreYiHd`*{0{XpyIR^u1kq3pl}4UcvsKh*1Sm=_r>e z*S#P3&j-CuKNzIXuRFPDOt*i$Uj*9@JVaw&?squ@xW8;u<6}T5XH$~X4uv$e#fCs9 zSOQ(N7pp*p-hUuC-m2!m>BFHsRJEN;FP%ZM$&(Opa3kBA4b@)X1Sy2wF67*3r_d+s z@f%7L>)XDZ+PzaIP2@d*kA@J_7pL#p59J8DJD8_&HU@`;Kq_-EImu_m500VVO+dW? zntdesvMpF9J+sR2gPc#E0c0UdmOA&j_Qz98O+g$XE^U| z+e*~nK8dfezRt^6f9T-s_#TjgbHn}oTj&Z3q!OG_%y){08nB$vr&KoFGKzLa0JlTH z^o>9Bg*>l(cyopBcuxBm8JE3cb|=rECVTd`wD{FQlAXs3-Gvet+eC#T{`}t~w z`-j*$J10Z)tObun8xav2BUfedDZ|pnf(h*CfD=lsyJ7=M-Amdd=@^3azg>+6G)1Ys z@}n^0_95kB8^Mv+Pzl01BO#2AUaao^57e>rWB?Z=Y;+#U902KihbbiQGW)V8aE9iE zXR{qhb0iWY^7}q;i8JmL=F`Aa7lC8>2lD8E?sy`x-}|kK*@6K9&}8FMh}4uGExL}G zNyystCH&a%MONgoV_qsRp?yXT2P5qoIgZK(d<&r(-nD`EpYsA27<$mJ?xh8%+Kq4C z^ycW*(FH-}1pV2R5DU z^dUw?z|G0v=EQPGK{%qJ7~st-Hu~07Z@h#7n6nD42oPbUo?FU`cCm{K`d^C&dkUBQ zNAZ1{#hvz>)+T!SZ7O1-nBV;+c0p*rrOrXG|1~tO3VJUTncmsc$_bl>7r1wEezI>tb)AVDyJTC*5lU{xh`OrfY zFTlaqW&Mm7WIHGg!?je?)Akott3oX@G6P6DD7( z4{p41|Nb4s`Ik=^W^ckvks@Dl_O78)n;_a~&78nn|cMbDwYyo3M{u ztJ{D7C;WcyF0#)k{r!yY`{R5DU?~B<%{B%fQ|>pH#Xa25n63T_z_{R;L!8u*;>E^b zcY0Hw^!L-xij@2XnAV=!3d&f4wr}N z_vFi*E#^g=KTyRkyj54s@TwA3f2SXsP!~>DngT{S|jM7)_|XICV&Y&xZ4tK;@#X@?oz zhWcfG^PT+mqDJ7Yq9BOK;kbQT#CQ0W&vEKZ=@6^;B@pQpT^|v&& zXwdxo^?0!mn`m6YHFHW0fRZ5=4!;3`exixLprc>}P)zV>Y&f;%b_g6*$q-l7iL9ag zVsTb69fmULwt~ZlM>3=GBwVev*>}I%@$$^vkPjQ$8P^^f9h%-eZhkj`yqR?DZ9www zYVFdpnDIfICjAD*O~Qz3K>nad^6=1XvNv+A2~|pd)iq#K{=T0W1ED{o&||^2kYhx_ z)PD31=DFr_%eDC()XI>A3~Gc>cq*e5Dgydg`X8Yt+HKJFwmVz0F{yi+cxdH}YM$>!p}+ibrSbKO$AK z^B~TV!gDAOsp@nFIrnLVAbep#>-uENYsOy#Te_gxv9;-1_2w!ERSUKW z91@y)u&n(Oey)g8>fkKpVgx1-mCu^M$=&GbJ+9@kk945+-J zO9NTCUCuDLvzZ?4mPF^=+COBi?YTB;A8U^9p6WjMtC+#L3NI?rdu?~)J?wMOe6p8_ zkk^6)oFPYgMpNqBcqvK=uL`iV2ClcO^0`GCj;fDyzbW)eb_!4qO!U|7rz7s#;(>8D zpk`%PU;}86<7>_&n5vbfE_!PTufUlO@_cThk!#z31TSbKLyT~k28W=6(MfIc(?DeU zJ(Vy2>_nnpMhKVL%z0*gF}tI~TUOusDvw7@D(-SI-_b1L#}vz0-@_*X-l6pRCt8c} zrRDD{5nC!Djrm0v3JU%`I!Ro!X}`7-&kEVy3ZrgpCA|9RwBD{Joq^NopG=mJEB9f8 z$pilw3s<^FH9eus&N3Na$##?D^9N}7rR6uDz9W{G;3F#_gV!8k{GKq>R9CsRWAQ0RL62_?H5S5(!=`yK)C+wW{1eA&!_TrOI z?UHt>gOJ6c)R)~}b1v+cK5%vV3s9Eea=*hErIKXWlt=<5t=?g8sBcQj>;+XYLjRjk zPp{U>uiQYG4BMLzbdM)PKO)yR22!!jp8ftHU5e!{9N3BZc8^axzo4zn5+*zV@H)S> zN@4QG+f5UAuFzosf@GgW#o3Md)yliy-GmN=L`W(pl>Xf0DP%R@CgaE(P2@>#mJWkY z5tR)1fw99M8J0i+acSzAe-~{WKVO}n?%Ix=`1ZuVBB`Oh#)~ZXabXGUl4N%#ALT#` zwTNeY<{LG})SuR~ItbZIHp%`F*S`&7uMI8~wEIXoL^xXL+iQww=?iNh>bECv)mN9G zYp79fEX6>!aXMbh(<0Oy5hQXh;VtNh&GSod{~YhGj}PeReqMj}_8#Tc$8)ImITJDM zXt{iHZeffdOWKw&D5mwN>x(2@41jAzcJ$ivoOe}IeaNeAq+lt#lU8mEN=O$5{))i( zRfeUkHBZJRQe8Jsj0(sxMlD(`!Q$xx>nr~z(F5T*!tOwh5C?E7cc54k6U5zp^lpJ& zFRzB(7xK@^9DDE{lIeM8Z{cXV%t?6so33+vhn7&zDIc{ob}3WIz-lc;Lck~Sbpz}k z(Sn|)Wpa~(I>g~v^hiQfFHcRLkD#hU&;F9B-zooROLFSq(l7sfE2^J-!+2BvZkLQF znrF=;c$+mNRT?R!M>CK?c~(q&cTg_~y%~QM&ON)fIzJ`*ulC{w;G{F^EtmYpA*>ek z8<_C74j+fVNYOccIu0Mu|6}m_1ueaduS7yWkMiO_`D4TRyAS+$qF)LJ;5s%h9SmRk z4}`-O-4BT@{h)SlSS;dt>T5HNCZF^R8>a3qu#5a!^~a5|t$0`8$Ob%R4oR!^q(>tX zaT20pAX{}QTfO|jA1^QQ6nahLV-Es0$ET+TOO>9QJA`~JH#rXaS%E8DBcy!hM2&@*gbk>4sbO+Vo+m9lDIpB_{~DquF;cXMo3TBkHrwKd z-!GTyoYH_rl_IwT6%f971O)5r`}YfuZ}@!gKWUvGzzWgroTm-;LjsQe_E*^Dws%S& z3nDN;!2|xUj_-#D0udyUJQJ~0p_sWo7+BfZPQt*DO7E$>1eBZWxJKeu>Ra(8_Zl|N zH7sS*or5=KKvHdfQbu<4M5dZ%EQrzt!9fmoHrHtNQTI~?2(PttJ#!?_UofO04ZuD* zf*_@lTY_)uX9M=g-FsN}%mwWYKzlF@y_5Lfs6j3$MQt|&xh1bs&HKOQJZSvyY_ZrN`}7YWqs!XOv~_PAWffkq~L3lca$05 zf*#*Bl6pP03xoIkwYzq!-G^_OFI35isniI_h1q9HxrCU=lH2r4^VI2@fVIX5FRx3h z_1t>&tZTx5pn}$ZLtO)xN%LgC;7rZhg?zlJn*>ZqZeWOr^?&i3Nc;1=R&-oQKZsY4 zeI3Ifn3{iZV)on6+;r+w>Q++CFKm-UtkRP@_Vw$6qE^P~6u4v$=T-_VE4q+Wa9_EmZvo3eE(~ zKs-Ro3t_fMA(?$Jfm8Wn_aX%lV4`hhQ^oUSQf*DOUk66;=GkkedIO=~88((($0NF| zQl{Xssf(=RP@@#gk1GncS-X$ko05TBsxgik?%O8ymG-!>vaPQ z>Pt@1p+=eyO!$7vJ#{lMeksZ=1GELQuGDzgbA>?EWTquS0c-qwgLAafOc1NbGKbE; ziKHb>^|-~U-o4;G0ZE8t3w1*hDd;KSx=*NLpoT`JP8mSaa2^C0QGlOa6*># zWxYAt*JE>f!!uvh?9kR5i9i!Urvw<6t(^PO4E1`aSgnF5Xl2&Igq4pZTQ=iE~)U=6;J}XDSXn zkB9`(j0)1*Zk(4sr)Z#=X?8Ze9(&kjZQV9*NKfcGi%&WSAe@p1jX@{gvj(a_$uO#UGEViEIyVYT(nLIBbZrhjV|b|B*Z3J#!eAeH)fOQ zt-78*EbEhs=GX7GyGUPjH?mhZ6jXpIELTRv>)-5x{^EHgF=Zn-jIyQTa`PgG-g4}B z@3zqvow7uck#+HUy7N!Q*d5rpbGDlwW{xgjZY_Gil9yx-(w{}OzfcQPkKy}^_qmr> z6SITceUP#seoOw*jnqR{guuGk2Aqz`o>Od=jx$pcfdeuCt_+~T9`Fx+Q;|fPOa>G> zN9=i-pqF3)x0e=$q()@W1IlhH0~HOv9q@a=x*j@G5&45^1(*v;+R@=17RNOHeOGj) z@cL}W0!o-~iO0%!dGLRa?`PSYlk21a%Z}UA-?pf}3b$5IkG!XTbl`=1h~KB3P`Zl( z>*Z(iDm)df-N(*TsKaP~?Ed;+SY|}c2 z+LQblVpUPZv{}s*JdN_HQ;Zh+UPZssQY!7UC8(sO?KM6%8Q=oI&><^HGQke7Vebek zy6-T8)n<=VNSB+iJAFt#c zbAMo_-3Q)v4mDjd_xwFo0i|}oNe=4{+6`mz#v%u6W1m3g{8%0$a9xl$>v~?h7!Xvu zE!!F&i>G6uCFnWxVPbyCGRKxcE4s-?L$2R&B=FV<%6;aylZ0>0Gpxwy1Xqu4r@qHB zirM$mg$9Nyvv|{uQ8h2s>SEQvzx}Oio2Po#8eXik(DN)Rg=2s3jV#ghPR#~p<7#7` za8M@mcu~)2f*v2c-Wzzt0j)(wWXXI>5J0F0i!Y}7?+cS))dq87<*@oeIe}D<-PWxH z$n2rcPgnYlkAR`wT#;X+kxjQt5udTh6q5AY%&?iby}ARqV7ZLQUw`=1DuY&L6#?BxLf8;*S)G$U{uNlE`)2(+$P`{%A_072(PaT_lV|R+d4E5qC0r1I2huj@Zz( zx}^P+bobyaDvhaKq%IAUj}}(9>y%Y!9$ls-#!69wcHQ zo6HolEjIW3MJqMDUi44V#|`Ju==+RtJ#}9&PW-7*V(n3l{;rEo<1=aZS zQ~z+E>_WufE}=hFzh=eD$sGoT@uiZY@AQ8x%AbII8U%1_HCwP!- z86KzXyfKYj@UG5Qr(N$=a`>O77(-;u2MSWUe$*HxnEcCh5L7J70D$@5kded*26{C%0+WQn z*ELV?H-_?U%}My86{1Dr8?F1!kPDFF@4PV;)*4jSm(vLW3ESN5ChV>kmK8Ig`}8KC zWT)fOGH(RT*VMhYDhM?LzUy(5gfH7iY;vt%o0_T|EjSpz`|r-jc- zf_bg&Wxs?3SP{W@00^}mXo@hf9Hq2&a_dyg?Y+nYXv5pn_$bl3y!XN!8PwHO!u0|q z_r2|07snj49awUc!kWfIaEuz?`bp-Wba|5F*m)BPiQ{kY>`=-D?3_=CgMX#?-ojsG zw`~_hsfpw0a`NQw63o(|hB!48ko;%9J2gs9DJp@+BYr(Xq#B|ml{buBRaO*S<$Elf zQPC~sRGQX2iF26LA+7$hxrZsI)Vt9?9{=;-6H8Ogzd|PG_qZLmcUf;(P?`FM&(|1w z+hj<=L?oPQDYx*8F_wg9gsLCiVj2!``D0ZN>&BJ~ysrHRdU7d2^;17nE=yZNB3?Pd zcI*bo91)V>-rWV?#ByTT4D>8~B2usTU_Wkq3-LjKs0k8VQL`v0?|QnVbZ{oab#Ky_ zd$_i`0t=5EA3B6g7uBz{Rv&+n)5qWCJ%`)ns;9 z@{MyD6yUu2FUgJGQ|R7x1A{$F=2!qEhQGL%Ti-EHsb3?ntbRG~0lWJzNZ6{hv}}ek zhW;n@qT=+|KUq~@8F&<6${XKtfHzOuVK&)b-5n`FQ(DHzilv>W&Hn46L3J?BBB+$x zl??5>w4A)bP5o-_TUgd_=a8fsg0I~4CS|1`_;%!^(Ywi=Bp^W>esXm z1OzmEx~p@XRQZ;;bStn+p;R%|ku(I7Ga~}+E5=;lPWn<@9o`=P)iU7uTJwbW{B-MQ zrS&$IEZRaM_2ao>j$xkgoq~)BQ{WZA$;v!GZ2ew**g)@*~nMQg9r3N-FGqu zmsHZSXUP4qOT0x(I$Nu=@p^x;sP)V|dcKVwt{hC`agJ5(2t1nVcKt!D<}x1@qQq+A zLiT4_3CTyo4K*`xcX=~*@2v|jrC4P3FHXD!bdcNAE);85tL#Kl;p3hU2Ey)g8y-jI znu9kBzMl)+`V?v&21LtLqGPjf?T ziloa(_<&$wv8%bCbHx8CF=}Ph?;qCfF~SEXkeqnU?%lQ3$T8abuxbxb#W}QefpHU*b?x{2a=PtoaXlGQjOChe5?ekz(tJvRi8cq*LWm%J3O;6mraxCa z2>zY+U!UU1R=H=96{|gjfO?zG5o-Nv=Y_p`2173Tg+3O);(m?U@gK+$&ScS?-T}d6 z#)6zjp2^DDGp2(#BsX^D`J;JxYKGcNt3$tPbWxY4H@}-^UF7#TQXF6q!5cNqZ_!{I z@GQ-9>7_*Vci_{2wXHMiNnv$pXdNW7z6IyCT7S6p)h44j`WG@{0Z`)gN|?=&bGTAu zd-)XrUjHpnK;KX(pqM9@0ZCG}vw7)>pX{^-0^Maa0dxJ(1#!FqU-P!b+y5c)BR9(? z3xHflAYE10w`dk}K~Uv55I7a0SSVp&6ZwMwo`Nq+#Cm~~{Ko-MKymuU)bJO3j6H!A zEje@~L!UR|;o|vHFjo*hGtAq@Z4tZi-pN2jv50WJjqk^-f<3WR@f0UID(~0}=xa3Q z3DmlN8(;NRc4Q|(&thwBf;qj?d20yXB)1`3^cvQa=5B3s6Bg%Np!wd-bnXU=iI<~f=_W>Pco+NS+B-U$$2?;1dk%UO3X zDv6nTz6*#KWH+znOVJxu|ANg8JG=4(~A{FNWEl zJgVZ|EA8>H$6ZQ{p8%OTFRamwj3TPt0xCjQ1AcO1)z80ljCDypUHs{VoT(9iul5Ib zkm4QXPo@9UKGOai4?`T{tUo6bug+Zjm}xHHjO<@G?9^IKZ6X_!;b~v5`ZeFXMdx380qKIXXXrS zbqz*FWda5IEgDfKV(S1Sx0mu9Iq=Lp`a1*vyFc`S%b0tR)yB)qcZ)8%lYJ5MkfmQ3 zDFYAbb5a+6^L-oCI82x9?`Nl`AgW{esl(6h1zH4Y&csU)q4C;Nu=I4Xf!&ULCG;$b zaS#X8d=mfg3egZE;mSmLh8#rOg_R`Ev^V*c7L+zDJTw1uH)R^VO^!bOmuv zg7yN)K=jlu7Z{*4cJUn}6T^EGhsyZ*zbNUn<~#z(>PP9j2Fqy>Q6(C2G{4;GIS^RB zg5{b?=pDRiWgI4nN}auB0)*dPNWb=%jRRO1OtH5!#~O(Brttli|L1OfJWii%w*rb& zw{XxKbLv2T^~w!;K2A$eGmg)30Hj!|C|<);Q2~Imkl*vAozV*f!kdv&)X?GE{RAqi zbHv8QRq=YpDHWwQ&3mdw1HbZ+K_6a64vmwBYqr1Sv*l`wJ_6!-(kxQnOqY-%%GTx0h-XVZag>9-PqXH} zjKqcqRjK#f?LMZ7U}PQ5vjq8gcyq3{sVFv9el@QnQKyFh+JtiRf=0b560+d$mI>%OfIo(ef6GgVs`{eH+9s*N*kW{rS7R)lmtxmi4d~bmO z39<=KjQVU#?w(_syR0{WAeFgp$FukrHU~MiCQX>#_^`di$IUPSY154>IWP(EjF3tt z;roSlN?zsBc1CFHHgJ#-TZ0AR9^DDPn};}m`c?Irlk8u4m}!aR8%K3U??L-y=aWNJ z9mUhN3LBWs zp9rG%Ls~(C{_giK*&R+lIlRUw)AJw3$qOGOT%<2JGR1Qn(tQ^~MMU_86PJ1Jl- zBJ?1>Rv^*ZhfX<*do_Wv4**p4wRic*IN?X=6zCy<>o!GqemWr zkEgNo2FiYmXnPM9M``bu>dQwDwtpouI@sOseI}R4I>l;Aa_OVdHF4T_?ckc?j<^w1ZiyjX2jQY3c~sCTTzpnMow*+H0@R@v<|4%OYi%rBJp-0yYnzG#EjqwH9VREQ0nt z+4%0Ffyy%2l)_BedTdI%6|&;3f>#Fz0A?kCm&jGP&4U9e;?nU@$Q z^s&5O;-}OezYqZz7nocsq8v_?ei9iQ12|!s+n5TIQ$B;q-pK5Q${xeT^9;@-i#H2` za_Y)$=Pqvouk^@iEmms@1kd)4^!}WpTAeyCF&R`8h;&*Kfg@}6QiUn(G*+5zxM%G2 z9ve`x>iv+y$nRu~+5{3xU#;Zvq`$Q7EmA81Tm*aga%#aE73+B~XH)($J&F~4N*s((u ziCY@jgoa(<9$%~<-|^Ke?|TK`Jd&YUtGWA3jymWwNZ=I|M9C#qg&UG&s^HZ+2;Am2 ziIzVMM4H)In#gE4fC|a{$^DY6ygZ4&4Ma!3q0lZ%?^7DOx5-(P($h<(phu zCVw1n^Tx}>VQ0$OZD(`|b$LJh=TS3oAyn-ATXEEbWpaB780M4i!#nH2Ad3AD^e}i1 zy@lbzw^JUJm6e3~~33+v@5cP*rl4B92Cn2zt!f z2_y$}+7FtUr)rD=*4aG>T=C?K2$JttE}%tA{3I=m=&v7C*&z>_j#i?As$6`UYG9ntMzVi>7oL-2mThfMuKfO1ftqe#QP2K}959m}|3)3WHs*X9vNc_U!-pR%<)=ljXm?%B5?QGnjn5Mh2}xBy z@$dhBkD1j&OOX}+46bW{o;iehm0HAqPuD@?f-Kkt=+oAf%al{Sn@xpvR1L0qDZ$3Pn^S(1E%Sliuaq1ib+wMNAnDE z5fT!sGDmUCq;t}9`8M_*oEFT~+6o;GFofpwDqd!C5TqzW-d$<|c|9uK#Urmqt&Ae0 zu{B{|)s10Hk+4uRJm0X*pTh3k{09V)SxX`H(3h4*R_-3OFV}L7=BYY|b=zd(t4lK~ zi$-$NT_1!rH7h#aC;J@}-v%l%4w6B1wAyO*)PZ#T_YS~v@SMaheI#LD%otr5&PO)N z%PBZ*$UXz>unY3SOhTawzCS29%?QjkS5I-S>e%GC<7;x%wLLTYHx=2G!V7oal?PS5 z%V<%`G3)m^PW~5}v|uJ^_R09U(`wCZ|21m(S(5Xp7lRfqSFac<@9Q-+v`?wmuh^|m zlAUN1Zzs1k3c{`K9L=dNDHj}WIg;JEQ1MizG9yTvrD?1DPm-Mr878! zlf0_YGaR_WiqNF<8yD#cTY0~lsNQ8yjj^YPdQ>zZ&)T*uZ74Vt zSk?j4E-7tAB~QTUOy0lppJs9O8q2$0s~jkG$gR0F(>-3oMqRoqY7_Q8s?;dQ;7s}t z-qo6t1Wq?KXFlv=^%%cNiV$&t+oGxABGMo@oG}Y?7?e34t`VE$zv~6*K)a6bNSdD3 z)Kq7DAGN+8)z1x8r)6wvDgGgp6f^C6dZK2Pt!i{Gk3dt5wGdDQ%IiaZP)$UKkQ=v- z;m8U4tC7>_ns2OIwR&kS5g+#yLzd;k^{?ek$`(3`Qm;3&sdf8D$NK= zUs})62uP82v<+&_#9If8LU=jlHwQeHlRlkibf9#^$bSJ`<*e1)d zugdZ-H5iy52IJ^p_y65xhM&ypT8ASM-SZ8WfU(KtvTP3!iU1CAGdJO55;zaZrA2ls z<*-{vGpO}jzI$D)H#^Q#yMC|r@|??NblQ~jFj1Sd#QS1m8#QkPzG;d6^)sw)I>}n^ zC=2Zisl}>#m=M`T!_WvAtWT^U9ZXz`urY6b&ZqgTGut6Q?;cM9**Tu~;7u1;@R23KjME7H4mUaRH-PV%yvO=reR{xkt(GWvM_xkM z?RC{+QjrJpC--8sseOavG!ol29v9Oa6Q7y0=;|}R5LhbEBW$zO(Y3pJ5VnS1y7wAY z!f0%BKh8gmj4Y;-?vUXt$dzj8rmw)?v$FDSly0b}8unEyaT4AR`uUCAd6EAP^N z=QMwlP`|u@M{ZZD2N1DNZ}x}OM*vwr^OOSL)J$8|(iK%(zcZx`%L?t3i7&= zxTMu2AoFgUY^Ruusdzv+h>JZjXK2%tapNBz8{KoEDq1RHL3GNXc@sb2jXT7jHMumq zmaub;+KPU)x`VFYxl^b-ZwwA};#T*9>a#S9IMIDHhPI2)pu5%?I=pGR`$a6Cv|q+r zjX7!H?sy(-{&cedWx5?*F)vb+%?*|n&1fbkoh1)ng04n-waERF_w?d8nA`N|OV};D zQ;b}`L0n7}^Vxu)4@=*bHU38FsN1GSS_Y)FTl&2W4N;C^=yd^nA{oeP`F_sWb>h$5 zwFz!F;vlGuzSEilqzlxyOxLk-chWCz%IDgu04juELutLifnpJ+l+{eXV*&+;iHiq` z9a~crU2g-7O^Aep7je{kSW#dG#Cb}Ss?vsY@dp1RUq)WEh9sT!CSL*^TJM275?j__ z3GF0^YKeW?Fe0bIYzCFJKnIKlTV69$bHCtk%=%4$U-=QBle}I329FqS-J>#tre-7y zpUEuU*d5j%A2qzM`aqk&A~_)ypPxrY0PPU1fMgbPSdsnYhIA%Dd*q$mhR?xRi9#u# znKebYNdBhkZ-ffKjGSMXU%XX9-?P@Qi2ZW1pxN9}On89@CaI8Rqke3rH;gaZIqRBr z9gsOgeK7X2pyUjm0(Jj?G`)u-mGA#Qeq`^mg(%9-sElmAGc%G=c19)6v9iZ8qLe)w zR!NdL_QAoiGL9AEaE@cdv5&(s569_u_xgN)|A6CO*L6J~lhmGv9^9^8>(R)+agH~} zRr?OIV0x78Lhv$8T!5ZWK{0?so}3x87ZBmYj2Lc3DlyJ!q3F^7Hk9gbTT;LQ5#66&oZ1LH%pbmlS`pXwC#sa5lBaL|1 zSc-0q=FT(a`sOTrLkn&T{rR+k7-d>9sN^cBFVay^lAlWx=F|*;;BkHdx>EcTHn6OT z?agRRG+*{>wvPgzVFGHRArec^2lJ^iABEO@BSStf)?6y`zZ zf{7v6uX#}}2o&Cxfgg|Isj)Q2vyNFl6&F^<*XsH-UU+ufYV7(v`T$umt1havX0At@ zfKBPh6jB#hqJ@1c?tN}Nca&BDPex^(cwa#u(8Z;eaU@Opf}gh9QM-A5w_dKT@=` zJvwO|msaPST+g$zEl7zfl4B$jA0WgU{r`=`-BIW#{%?FKz|gu`HsAR1Cm9!?TrC_w zcwtmfj2E=L44d=uVf>(xT8i$1;iD8*SK5Lss^XdGK&WrGIhbZS1!!41R`a=-aKDl- zik!MkVgV%k&25VL2=)aM2E(u1g%jEE4?W^rFTjNo^+fbgY@7iqv3d`@+MU8HZ_Qy| zz?Z<3S5ix&K}j@lZKQ++^|EsSBimwV?g%ob%8rq2?CtN&I|B*aN^c=eDR@++OQX+Y7?Ml}86qS(d*b9Ifo0M*^xb+KqYn zLetX9=IFhG;=qvc;alL~wVWS7nC5WngB(zW`&W{l+#PqCnyz#Wc9a^U+E4mQTeN*c zX)f^Fjqt9c7TafbS)U~R30OHpNk5PZAeFaH%MPIFxG4yqxH~nty$28n*Oue%xgKa$ z2QTETaZIsUsE_*h1_Xbt`T#sb#3Y2(R0B7+t+NB(n^+xwlBmJ2rV(HaVu)sXFyF4o z&lo7Hq^6ncf&D%=)rgHcJ5_MH%Oy5#U`eoKjT}Nmz~+}1++%)NsEOb7j>>7BCx@M! zXuUDMg=iyqN2^})csvrlJ1~l}qeU0fmJKnxO)q$iMIq+**XQvOk;IGl^L+cvpibKM z+HukvZ!=;{b3mDXAXLV5#$I5$1}$7hu=ELW?kof_DsoIAGXeN_$3J&fcH+~vpPqYv zuaQtQEBq#>%O-M(9JL&0C!ae*fdcAY#5akUKm^{$y+#tx#$sn|zYTcq;p-El-YBQj z*NlZ>bjt4XXG`#`7-RR8;w5!*Y^i4y0fB`gz5~F<+D|!#;B=bhtio3rStFsNLa~xa z!BVjABp7foPed7fIKZ5tBV2%|dqvmC7sBJ}E;O(SDCrnF`{qW)z@O$YzCg(f)DPG8 zq7L0ykcpj)cDoVNkdP~P7W?pquUd|c^vFWSY&jYhAf2`+VH-Y zS;HE{F_zxp$Kb~=Ddypk%Bh(Xae#2xB$L<4sk7ycaLi^S5jJRn{GX!gkoCU%1Y``I z)%;N?QJ-ZYPbELCX1?63dCfW;{oXS7SJIF=4<= zFSt%(o}1c)=Cqr(wL~7tZ$@$BXCeo*Ope(Y-0Obz4m$W;pCe?@m4wrK zeuDs>A_6~f>}>O{BGK?FN!KUNAyJ$yfO$z6AI_z>sb~58u%Q5cK$?^fULVJKa@DeB zAifD3xJX>m-T}h$nee(2@%d8HWfhpS5DK6_i*HBdws!O@_K9Wn8jEko$LCiulTB5E zR~ZG1{Va$M7z|ZmcVZ~Hp8>>})ug@@%)}_Lqz-zVr^)4KF2&RQqpFd7MkLlwG&VNz z*|6tHNYnYyuK1+j7^0m^ywqtwr<=Sv1Cyvo=Ys33c5u`w)J36F`d(!{FPP*?0~c;o zCnr78Qc7KdX@W=0?Sk6?_V5I$omehWe}G1QEy_i#2e|4vCJJ0NXB3svd4Y6{KdNR8 z5$I<4f_yKzid^(0pPjyMx>=7v!e>Ngk+)02aQ&SN|2Eo z!!(He5X^}z)hpWBe!5V4GQU0<#J+c)B{P;MK&d`R94`iVt7RYjTmGS~a)jvrG-l>? zO@WtbVh5}}HSC0=X5C}-SXj9WW;)#Z{#*FFtZiG5x=L+@4r+? z=Y`J+#P|Mo0pi1=UQ(7mF-4FrwEQD8pn02erd~X->Tlvh9xQ^ks^S9jZLb9vJOEu6 z#G2P85IBYcxnrflc?V}va}@zLDT#QHhI=~@5hEIB2<~9tM5qfTFueH?a@2YnV(YLm zA5!+NFTs3j`gGbg`FqMnnAn!3X^cvopMhdWR+H?EL{(|sUR1EX@1E(l3c9U8v=P11F08UbNKCBW?HRXJ-Fga23?^vYA7vr-%( zq#c;r6_B;KZ+%y4EGvaBYtcOuAXq9lFS|=nIs)_eKCnA|jN*pA0#?+e41v`laV`*c zP?n9GhL=~lMMhG z|Fr%;UeNxzh3WH?FtW>m%atEhiw_v(j4e6-7wZ@7k%A=|*-1$0gsRYf2M*n&E5o=Splp*7U1z z3)^Dss5N6pStU39#SnU34w|2Ir$%2J>ZtYhl4Z~~dFD&{8z4(-*KF2A7U-)G90?Vt z2Z}FTj%kqsqIn1VVR^WQqv$C3P1E-jGo&fC!i?W$58n28TCt14uCHHt+6mzx_hX2O z?kS9x#&Q+>#54iz@L~*s-6N9-?YT_Zt8(T-URNK;BK+9nzB8ry(LS5KbMs?dMj}J` zb!$9sWZirX%xpp(@6Ff1{VGE1Q#rd8Q2l#A$-~5;+ z9QHB)x}!(cEaL!+9*YAcR03qieyb%KIWghe2l+B!uduToU6hJw4qtopMqk_0^(B~% z1C&ggf89=#Hs%ifljZZEvJmeVZ9s}uQQExQdeb-W5-?fg5F7|KtybP6ZB-XMLWV@Q*vzmut-Uz8sZ z#;*JPM8^rxNwAYB+?idU&`L$k={ASCxLipYluB~BrPJF-lo${?VGv3Z0CSgkfmm+z`;$yFlIb}$ONJY?hOV*i`fwD+;}CZs;KN0uIx zGJnY@vmoYv`e)Kz!c!s0Bip%hgo~-~rM7GX4NzJ(^$OtW=5|%bCMVZm7nLB^3ONfCyJ3W8!|!2oGGNM$U$ zV;b>cjetHGKeUHt7{7P7>`7Vs(UYyFQM0#NJ_1s^+8Cd@H7r}7yEUR*8$5Orv>9~? zseS75F5R)^2RJjiYV8x}HY!J=TrZPR{Xq`KnD#8s0}c#x?DYr0qtij&dH6ft!X`Qw z=h9wKRT%*#jD2~742dv#BmnWzI8XC|Qg zs;vk@x<4&U5DGcJ9QiYK{9JyHBS&`(Qu$?Mn?@~$y?AHtF-_{WN=h+9Ul@(pg~SmC zKW0GPw*x4kmWOkGNAXSJ_9x69>}^KT+p(Csd&$>>8JB6#Vi1Aw;03$KxY2vRJf-KO zZnhtt?!N(och|imhGy|h0F^d3q+eUjqW{ua7t_j)co69MHt2h%$rRu=bN&!{rnRXm znB1^pVXz-{v$hdEiF4S-&sjCDO#ghOE9Gw16Nj*KoekifG>`VL&*w058t{$+dq#zm ziM|bH4uvF`v9MN12foT6#({ z-BTF*l{>C0GW}5$0O=QWhV4Jd)GK}kleL?lH~~!HobU9B0I;4XWv=43{t_e+^HrvflRm z);Ty3(m*BHQ0?|7Sxf>_+Sa}5NMq0TgH6sfkIc@k*JWc&@(ijsy+RS^bGkS7OPxeb!3ogQ;QFA>vHI7wK~I-E#5~ zjWuKrrRX;uZUG#i&R)ae1ba3-KksmmXH^a0 z_nluR%xsyA=UB9|w6EBS6=i3PsB2PI1?1wd2WIXwn{Uq9IRmL3CWF2WWrZ~YM@j`K zBQ;bm6ZVmfE*%^aYH29>SfKmY`J5sfz7r~Eh;Os+)KF4^hh+d@q2m5^DL6)@Wk*rD zl9UuGZnQB_JNV{ywut&-l&9N@zP`oi+PM40FDK@cfb)nLSpreGtwc3bdoOaI95Dt}c zHPDr#4vp%UD!2|1Qf=TTgZEKDmKjhyygp-pxE}QaYw6YPIenft^%tm*c18ZEE^E)A zi10KY4DelzK+K$~x`t#OA__3t>wHZ68Xpf~RoJOUL2&8!B3!--ES0CNVobq)90IrA z7+-Jy;6folyKqV8RYt* zRC1nxZe%|EwoLB>GFr-lBSoHxX9W401p_0Vg3DETPwDfo9*pqIaY}lzCI;vZFbZ=N z$(~aT0O%`VV{xmETTQ`GL{ZpzU{p0$t+DIYz{kh%`$;XhDe}k0SSROiFXS+;oZ~tw zXbdOrAiyLnw6*%9%>c$o!$PJT47-5r-|wC@au^a01G-2Fbre)+a-v$Ogr$t6R?M6@Ln`O<8t~yqQZ`5 zM6XZ*EDkQ(37tvTPWZzdlqN#4h`Q+Xq;FB7drhvvg0D0$PvJ0#_?oCtwN=k~&g*u= zZ}zgald|MnW-40V_g_rZKWmUh8_DkZUTC#jC}ORC=;Ok>dcNE32FreRv-ACgGPz*? z_kW&GmKrE^cs6-H;C0fk3|FeOx1Qo;A7y-9X;`2h$uAt5(XB0tAMbnm5H3@j|7@{Z z;L&)Y0{823mb%ir6FEkGoGgXCeyK98YmX=GSga~pvdWmLvd35|OZn-2O2l!^4+`Hg z&6m47!>zJGpXVYU>{@@YsmfY_#mxz)$ORQd>m!u3tnit1U8MBXlyR*IYVTaT(7uwXJ zniqMsKTR5GWSYL8k1xGt5{*^J6&%anxZf9HZ<{9cr;YPxyZY{xNc-cA)9_G!mt&j1 zAm;nO2%7FYd-sluc5h7>|DxQ|`ki$=*kjL_0%^ZQxm}m|XM2|@v1^_XR>D*QWE4zJ z*f-aja=wZb{KE%MC{#_3j96-EsM-In_tf!Lfz5#Ohfrvs5Sy8tDCb|$Q~b;m{>=FM zL*6FWEz4!RxysS0!j!7Uuy-q=6!W08-MZw!EzMb}sgk^a$2*T?OAIk)w}9n>pV8#N z)A`|0H22Y)QeK-|1}_&0m$;5Yc4b!7T1`(Ssk7}DcT?taXYU-Uj3`gpPiYsV@nKVzdX7 zMC;2WnY&6gwPoeIdvC6W+68pmEN4r6*TYz9oN-cFpu0zhF1wJ7MHlSH#gv1xxCV#Z ziG5=7#T(P!moB!o=6VarxmEA>y^{Bd)QY+}9CX|G^D)9?$2V!aQ0mIq3xe~bs~)FY zX}w6ECe`!RRO#hMP%gYxPtzT(g;};8av=jy-PmgIH1mhEih2smi}F2mMXn+1eY1i4 zgOVosn}!5Z+855Zv&{NmWqS&FJ9hh67sD>U{=>nPFPXn6-BNGk*sVA~eufIQ=aDgQ zu>FP~4=7%9-61DlYmf;zt3GzvO-G%xuGd z=ykcNCR3evA>CBKrT(sjI!CI|Nm^oPU1C$Wd?L78eS}u6W0-_$@wbA1UfI6&5fiL7z}b|>R@YQ4bNW;P6%vhXatnPXO;Ef!@aCi z#X6dlA?w`UXdlunO@D77b!1$P@mj13LI=4D9JSt_qDam6#bos8_yoI}AOeabcfuPD z<|>ja&$Tr~Bhf=Ijkv>vUXzXI7$()!M}@L$#OQsqQ_W{HJAN0gHzKQwCKGQcNvwK_ zrM$n2O5IF;InYm+IJ&bWswhcx9vN}6GsU@h9&JVu%Z1p^f7FxMu>fNBv>gk+EUFgIC5$8tGmnu64Y2Yj1GPy@*Em??^FUbcYIdRtr5s~!` z+oxev`>J@hnDm#7G}F7yAezMP`sA!k4ppFMxZ^qN%gXjQ^C5#Sqr|ozx$k{)ehzUP zhMGIKxgNwY0j-jDALwu9{2)ZnIl(QqPpRX6$uq8<8OG@W4}&^=5qm1yYXjn%uKysC zaY}Z{T?7eD*#}ahj%KT~F63G=8nD6xZ|`?@W%BGWOVkh(XrBpVg5UstDZ3w?7bWl) zbi?mKe#+U*ol8vWn$nK>1jZHAsvr*sp|lM^olhFK?u+@v{d}jC{MZpTq+9~n?-Z9a zZ+pk6znV5Y53%&|D zL`cu}mu3m2GvJ#5g8T9b4G`_Q9OP3+qpQU`Z&n4{BP(BMP=%mi!)=z_b%81q1wTyl z33T){sX~B{M_p|L=y{NZc3w^Rs2n6$BmB?AOb{LS=JM-!7=Ep{OeN?AAnO zLF>N6We+m>d(FmDN8Y_3t!Cg(&2PASwL#$_a}Wyq7%gK>YgYJCWfW zTRPEqT*xldM9>umEPO>%Ih|(=xTzCoS4x915ib8btldH`0v7ciV+l&nV)QOuqWXww z_%{tk`UjsFZ*m<@oRH2Kl|0pbs$DsLiFC-k7i5ob%8|{S9^qi5(f?o;IERVUP|&(w zb+Azc2C{KfBC#K&-c^fn`Gv5#sw_CjAwE8{p%oXy>)U*=*p9%ien$?L-T!$+cU|b2 zn)(wANQ_Z_R#DUQ70Ln?oKqYES#Lc3x}8^5^pV)shORqr#AL|QrzW12%E07vwrwO% z)2F9vkD13$Z0|?4AHIUvIpz#J&g#^W3Uck1I23*HG?i7wxY*kPXcItFg-)Uj2y;J! zGnp{8J1Wb|*FlyYX^7->ly>ref*xjscQ65LLqrY$VXZAo{SV+Pbmib;;hMGB_t9jr zgJ8*dO|Z_tLoI3*d^M!>WE%k#v{Jx)@A-WP6Rfl7YZHz!Wu z6j9{f{k;37PjG-N=63id_M1lvq4n_{(96G|3uH!AaOkH)B^B)~l+HNAVsJU~&nFMTelDeo&G0kha z&@}a~IA!bG<&x#X|EKAQf28sJpA!fmO{;KsL#Hp(RdeWxe*>~rqAjCsh56VxB{HXf zGJIDxegC`&HcxhNpj7hl-W}-(IUf_(v$Y{;!x8PrU?j1vw<^0Cz5XrQ6QV+3s_c7$l{8-(FcEY3Wma>U` z08&i(vvN5n`<#%>mt}~n+U4n(KDnmVIF|Ic1^X1jS+vOMSvR=;02=jAH(tYJgWWsN zp*t$*=;6UPqD!}MZdUlP6!p|f@uVHd5c5$D;G1vSYj_!>L}Q@mMtK@g zH8!Hk_aK>4Y+#9o5CGFC&5H)&ruQ|iBnclnbvyAe0nJQ3H8CY!CJiIuC4V4G0WclD z{NR1}X%K0Da-T{-ltrW&`wMibEWGj^Vui5yb)@AnZZAiEfsldlCw|A4wg;OS?DZCi z!yC7I4snrRJPL(mk4&Mgd76+l#-YGW8`G!YePUI+%Z|=1+2kxo7^U&R1?F5xygBFQ znQAavpsn9=-W>4n(_UiWhnXvVfd1?4oey|ASd~V_O_1tNi*Wwtcotom@82C518;*z z1dVVx65BIqiPcQ0Go62p9o1PT@-7kf0LzW`UWes$ex+SWX)JVjUeEywjAHtPtKQHhTT8yVWfX6Tcpd%hf9sVpPx0cQl3L7B}ONvJK66e56IZv))KqR zyK(w89P^>)Kfb2h-WuXj5}?iOk+v<-eiW$w93c-2nsDhhKMFYlf7=j&H-VL)-?{(gNCYyqIy)PHMnk}xr zVY12ssXslWHWD@?J{hQNPI%5Jm{l> z$FbCyMb8LAd(l}Pam*AS!68TU7;t8s3$6zF6{j+b943t3v&7hIx9h@cpJV5@i9E!w zMPHMXaMrSvA4UrCaXfwu<_pDz#hx|sam-%}4(gnd=yg%Em%nYd54Rfj_a{nxCkRSk zT#W>0niiJnmmr3IdLWtIPWR+xhI3f^!>J>?y|!(%^Wdu61*v7QMA$2hq|x85>P+XB zwyU-k?Se5hUSER3D{nS#VE5;Nwf@$R8)0Tc#*5~<3t4U9G?^jadM!YyF*!W6ts<00 zw1vt6gxG-4xEiK;uPlGBIbC|hE<}u$w0jB2;@^Tx#{5KbZUP8%%*}aOGeldFi$)}n zE1*yTed0&fVp9KO!@YP@V5F^$po~|{LOHP9rUdP*Y2-nRYxvG?R7ynUCA`(cv{fp zBj=M7K&0)0Z*06*6jt#(YhrPJ3>Qv5x^tBJDNWpeJ@wVf)tWudVn$F(+cW;T)9b)? z<^r2#**K>X3dV7wj2TzDuN%|tD7WsWN=e_Hw>=H2qi!d8t-?&(3y3gywqaBkWr#-@K()**r%YM=~|5<5VranYxjy-Y;p0_yG z((vB+K`!<}mC;HVJ-lQdU@-)uOOJL7M2GGPcE>v^T`Y(u2DJx$iNT9=>t+>Is4Oe7JMacTDZ0ip_dUq}`A*!d*cobCV zpC1HD={TDl{Pmy>`v%Cg-*f8dPY7AaWb&!{Bf`{4Gs3_$z=#*@a6D%!at75kBV3!D zs&djXFIt|gl;-`--E{6#w@0Fw;&LLBsfRSJjL9nK`x{_82Rh-q-$IS7c?XdK!UC?;q}PFy1?-r|!{+MgW_j0&=@HwLpZjKjAyz6xM(?Rh2Bu z<@Gb}^pq;06#_!uFYD3!F}cIbMP!}=S$d>s=vQi#3fFxHhe135#vb4P{J(%wlCfMs z`=X((mfZv+W8saC3ZY=L^`KSs?rG&w8b6CJSy$caXE+B_AESWUQrwM!-e)(gLMEw2 zJM%D8Y_7T77ikVPEzX@w$1?u8z~r;~O^j_k>L!F}+Y$yu9fjva81LQsZtn#P0pqq# z_8VXQ9jA6$s_Uz2@N`~N1`RgSg6ZGIFgQs1YA!uvRet+?UJJRN|;69ZDLperg)|9`s~u0;8WqInQ_62Etq3By&>tfOhR$WPI>?lBC1c({79^8eYz zCMelKrS0%>;}bH}A#z|bIZ@leK#y_N(G@g3fE#coafOO1c6b&CWSUhvSI$wvhap>q zg_*ybVkAliuMaK0MQTKjymMTL>oh=bS%{rWxWixJi=Z}4kWQ0q^$^dbN*$7=L3L8N z!MN`;f*V>Piv6y@y1P0tO1x<|DEZixnTCfT0a)g}FSRi;R&fB4aX0a`ZJ6;>=plCI z0PX~rZFx1|Vc0*b$A-MDkw+`(-^-yyy7aE=my=GI+C~8pIH$2g)ygJ~-acFF4nCWW z^FvMhe*B!y1M*`~1k7~5+~pXpvV7(1d8P{eaAir-^$x>gi1j|RBQ_^Bx) zuhb`wU@QI4)oy)(S5*7^#1_CaBez|_-7MO~JeP?G!b$Ckj+R!$=K4BWnw|Fgub~ZP zPI;uyN1a$j*2tyjDL+9#ui*z~%QxW)n}e%I-m`{Xq|QVEuA1c>*QV|qAho&oQVGjWu;Em5w3mV@_Qbd9Y>VJ#wCt+e?jN5G15?5;08CLZbU#`f&lWm zBM+<)@IN_gpn2OdbA~qMvr=L3DxLebDkBKgX|-Zs_IaHOyhv3)t0e69R8qyQ#07m+ zjGi44lw}kLGkF>XQ>9Mlm(GGWkDcW;D#1tdMC`8V>bHX+b&Sa;`54*@&D@a1rY96@*1_f{Xb1#KEVifw7%@uZuRi-fsjk_V zR$%744eKUOe{rJqb-aMeA?J+jO{OyNaCP9{6q*O-eiq{u*o z334>bksTQw{jPVo7J!+)RoJ!sxsVe#hzOafyKKms;S zC`K+mo34VUSwC}{V7h{Bg16Tu1PRrbb~(1par@Yn(R4O(+Q4{}tf;>HxhNYV`L7xW zh~2&MvAt*t*ACq|stS@DYShcA#5gyc*pgRK?I5WtlnO)KzI;Xj+v$CMAI!}fp;HKl;)TUUy9$m->_ zc-N8ZTKq|lKbi?V^d|DBBT!T?@r?(7`xcMamCmoy)*!EgI?{m=(qKj}NEu+KW<=`^26YSxb47Obaq%Z&4&LtkFy{#EuOB(geUQrkTxjI-j)834&DZb9uomJj-&!?=tsj@Z8&n7IF3?3})nT)= z=QTsXPTF)M2%C<)u2MCwWe`l626z0Q07rVDEVR$C9pOMk&gRVhNY`h&Bd$ytSCm1@ z%49G3`aCwthj_YyT+Jtd)e)FnkPeu6g<}n_cXwiF#8yL}Jf$PdS+nhR6}@K`4UtU` zmPKH;=SmM6+grSl2lYs$$in7M%twR8f^MNCD`6=ycczXgLNP~na`RQ}F7Ow1NohL; zJs#V4;K#Kk!eCH*O&uxjPDM3COeChtX?!v)=(K*!YrH#7SeVy;9x#_#ch>S>R*?~| zChM}z3VT!nGhm>`3^OzT+6M84bVKkF2uq_x6pc2RUq?guGZCLsy;n{jyQQi!Y0axbq z=$c-}Yx)qCUzK^5hGH=Ns^LyGSKSUqp>LdNElzOq{On|~OX4zjoxu-P!#sIX%`(A~ z$&yyF6W_k;Sg4Q$0;_m8<7G=nN4JIutf_+yHX7k|lvS%TpC5Ol z7x4ZlbNK1W0Yw_TcD}=%-J%M|+Aia#Ls%TvY^v=9T!uqKUJAYRZccJkbjgnFUcuu& z(t!*-)G&hYAR7NP)xP92d}k>yFmw6>ym@qcaMO}%_Ka?6&ZF0i{&(@w27-VRCIINp z2u`QBG8wgPaVagDJ%U>}_?S+a9igcr>rrzlQBx9J(y|x(V`9_sss5x)gzi1}WP&SA zXaAtQynMuFR4d|$qD|sy&d+Jw3&;A_1+^niv<8{4Q*B}~`f+#YF|$welk-8w9nK{y zo;QI75jYa}zEOUDWEw&JFu&&#b@p5NJjY>(N3gggMEDzP>}2qgc-RxU;`4qXz)Juu zi$}BSUx-5Fkomfa-Ed-QpG1+8n=;eGezn;p$XS~zq=Tkz(7m{W1%M%n6b80XD@S3D zHj_a7(sJc|2vUE1$X=D6neLmK`dqk_Mp93#uAEUPMlhc1LLC1=DUuz0bar@2*l0CE zsuvxAjg?u_%rSep?edOG5ql$!o!&rol5h-l4nN#Wjx&HTT!?#5)9?2%Kb;X}lK*Wf zt9RD*2JmnGGmi`rQMqrX(Bnt;4tA3#Umw14WLVtGtjOvVpf{8e$)n5nFDNxIdaOJ9 zrF|G!lCMsjIPe9g;8~gggKp--R42P?)uNd@YE ze)`GE@l85+1sFwT;=%-H_BeE9UM{!M9%q`GYQX^%9@utOzZid>Wmpe>Drh^as=Ph8 zEn@Ayg5iXOpD)h4DT0|WE28M+8KS{UZkob1Qi1Wwx#AG!u4Tx2at>=B&1-f&`CcnK zbL<+rrwBz2SJ0TPLHOMR3$phTMvC`h5g?*_C#EAlRp|}uYVF!QZhhP(#H?y-`n$f$ zP{Ao&nx!+*Pza~}F)ctpt>u|=#M}uOj1)ysCNbHIv$Syn<=c;J494O*NP~MS{xO{5 zSN%GGqA?&=;N*<6|1aoR%8oFzRFrVi6+Jv(yYjv&G;8UwycpVLyessR%Doe{+T&d` zv$t5Eo5#=k=UJ;r+11~LSB|s(uts>vQ}{pQ?B~3wmr|d#KK>J>YFz`LplHCTH~x$! z9aGsl|AO58Aoq?DJ(`2`NxdHV>aT*nQp;ae-eG?|T$9l~Rw1?McdLe{dCj&WCRH1{ z6?*T5e~@$!Wiw6f)$0HX4{3yD@Ntp9P2??~yK=RKG6}+Fa>6`+c7Hw0yU6W*L16Kd zd8xhCYWBv1#H(YumtY;209rrJF*0&DFX1}y7Qfc!*)1&p14{xBX^O^=<2MuckL#|# z{WD4*YQG}&dG~40dZZ&J?GW}lTGRV>RBnMgN2$N2ttjWdH@1nw?p3yN^1|4kvfD+a z*=-NB)%5&x$;ni8=|}ek{dQHscYpj?+8GtP|9GZ+%Ii(!%hjRZlC~?i%2zZ!vaY7o zuiIo_7O)^AC3nPo95?xs{y1y>1=;LAI@IRsu^Frw5lj?SsQN8#o~?AtuVH;qDM7}% z{EyP_otgz~}vsaOvY(bTz@e@Ly- z?iX7N#~0VqPf0HkIRjQQfPENo{(>#M(Z;-7_m5^MI z3{SOUnFYJdt;^Hlz^H}wDXJn(B)#Lh>jT{a)jOb5XrBAxRZ?Zb(x$MbTHs_+oLqNmy2 zK@h}H&~8uwM7UJWfUnWXQ1<82M9G^3sVS6y=uChV%qyuo;`Y1L|9#HPF-d>(=9PSX zTHljqTMhaon>wmUnOu;j>hC#8^+CmH45?fH%j!kH9`oc9nfnFCO%p-tS5_sK!fq*y zxB&NK$Jje90d27>bZE4jKuA6jXqjBzDN1TJ(@E5H|AOhwbT<17Vp8a3m)@^feazKU z{^c06xy7Gxhv&g@+!SYhTOU61?&6_m#@=eALjIpcyK^<69GND_SWNxa@J~avWTy#~co0D#%Z`ITV`}PmA$AaFGI`|hzzB7JD&!2;AaeEz6$ZC4qv*|F0KrVEin%gxhL#b|NQYTkH5_~%fu@!Da=;g zZZhgtoxR-ecLX5UmNetF2kfRcEEKv8S@VDYbPovoJ>nm#1Ge2Szy0Fd!VSk-&de0H zK6VRkxsl+~xRc)nk!GAWyOQ0&XIZp!&$NK`uwz6*bMPw;$epi=K8>YEsj?YL)%i zL@D>$Lm0B8d-^;12j$+5Z6j?$=n<=a=#U}1=krv?#HP*bpWCX$>ECurKj@koI{WaV zditXEh4(NpaVXVuzVGm&OH4;`ehG@bG_d~U-R?B`D0Jn#7E zdGL*yS-^UHtqu%KHt;|)w8N z>ITe+uh0XbW=y{05#A9MIG;0>mOVHw@Zhu&R^sb)DQzshHQ`~7MjlNfe^$DGK5a6{ zKUX|p)oqD~pFZUK#4v2o0b5I&bjEwZq36}bq^Q>!m_~~&^)ADMg_<4!ry(%;}KS8TN&5@I{O#cXc5Gx*8l^f0|2T01=)K=qz@Ks zHu%x7lGNn_w4Z>>fWBTx_frY5`19CnxXRKWmE-fk+sMB%mnzfQNL$bfmY->l+}@ZL z-_GA&&u8zk)KF=haLxl9eJAWjBpYTo-P2VK!5j`FM}pOtg_4MjrhH5Na4ax{t(L-$ zbY#5(tguQWAkHQ}auQj0GsdV7=W4uEl>O_0OBtEG1=NHOUv_p7&a{xSe1UP$NCLco z?tWtIU)*B%RDK+`okq57R#lzsn{<6OoEH@1(Cd@nFy*k5cx>e8pOLTRYCe1m(smL| z&6}eqiL->L_#eHajqNoQ&sh_aU>J12Y<)2k>@#`l;L`oIv92AcJby?bDtIwp6Zje` zC{E9MJv=Ui%}<$;Iw|UE0W2UromHKV7}CV>5yt&7cz^a zXW*x(kboJ9thAhJn`o=m)Nn;7JkZ(twWw@?y7J52Yk|8jFmbj!|Aj6s;EgBOio%rZ z;TQd0A0J$erK>sz(>1mfC>t_bhWO@Y-1i4AL zo?eo!M<2W`lBA*UlmEbJ{&Qn)8v!;7R*2}m>l`A|NqWb^{6w4>c^V^Q?dcc5c+#5D zoD~fJ2tPPeDD1MI-=W+-AO?9(jF{gg95D_}i}6|+&aHA>NDKjT;-X!$C+H=GuogKJ zRG^O56ql^!kaQzIGA4_3ptf+XBc9}0%iJ&jhQ{990_Fs8pfKNC#7w5c>H*hKu}r7@ zgZ|Sd^?_wdU(747VjX#%iZHLUE7#0^CTTDWjQla-rS+yKhwoJBD_2d#Ow;^_+=&~~ zWdYrg6MsP_P%R=l7cIn+SsKUS274)oH5{(NK4ZVUoY+YYJ|S8EhZI#|&o5)}jdKxX zFcqp*a=Sv7bIy+wrzXytVD-i>Gn^;@a#xpR;f7J@&*a;JNWvuw*Qh)qoh3T5Ts8r^ zBLRJQplr}wfHi*6 zf;J_eR#>?7EboM=! z{&U&0Jz#MiY4OyDAX88PBCdw+>G4D0B7iW#fwHlgw)s4Y>cO|43o_s2n@A6y$rfsF zs&I@Akef6g2gh{i6v8K8P>W!N=;Sq;NQGI6Ydl*b=W<2DdbRgFB1_+R{QvX=wH=B+ z_$2eKgH26THf5$`){l8(q$IIls3lpE{NFuRxmvqVMF5o7^;wk_a*~XJe=p%v9?0YS zZnHxMX?a;oRAH0KaT?Vbr2u%}0rLz|WVvSGaW#YJm8Hv*!EsD7G^`CZ`P^9@r=357 z-XfzB;5~R{JLv|9C7DBjjbS9r%`YCbR39-Bk{*@B-G zv%x(2FUaSxc8zK)*zkoMoO`)fC|G&$_d*D*j%*Vf$Wr&TljTJAIR(IPh@|z(DhJ!U zv#t5bY)V?3^ml|Ivm9H(QRB+mo{SP%rqHqN$_fXUD#Wwv4$E#g@>7fPx@&rp!kq6` zX^X}2JRBHR?BGEQr3kgKRSWJqo6Q)os(`1wp>Ymx3RoZ+sPXX122}WxkRw>AX*1Ik zT>%4|Z1z%UipAwDL3$e_iOAOT+HL?FIQqX2bz{EJvxaD2wBFde$=8E?^l77+A?_O1 z1)uDML8bx95BL8=8T;Q`RDH*K4Fgftl}9>VJsZIUh|yJD0SUv*zzsDC;Yc78SMwqx z;nIhw(Xujm*io4A*+&)&vmP!B)UpaRNk5Z;CnGsyPgPu^lAdy(5QJQpvWA_jBakYl$)Qv**h}l-KOm~?1q>H35*2YF%6Ap2 zfF{D{XpThRzt;SuPA3hb`CYYD-tQyK9iPdtdK?!0_Joqc-GXR1#4`3dP#8y{m`LzE=*7pgV<>;%OEdzAwO6So?$09AtC zzLA1;sL)uSj65V%q?OZnl8rkx(s1`1tl>fi;R`Rc&?{_A7|uW7N|pKDXgGBnwzcst zsn1`~J*DibUHP&|nZyO;T1m{8U)E8-Vc)?sGY2HxJQDxI?bljQDgy&E9Z;({uIosf zoKmtPUM;F9RW?*miQGSeo41ftLd2z}S7YwT#I#mlHA$Vur7pWO{##W}C~;-p*9cG- zNhH3k;7xISwbU##mj{U42wn!=^Ok!+{dDJdJr=|(f?vDcxqagzBc;Kx-@?}~kDOB-}yTXDGW zaSXTS{=m#WUC@ABMD@rD+w6yIryqGAH?a--Yg5>@63KkS;d){E0nT)R<-pjHP<3+y zyTW~DFQ8#`Zc?BkFk^1jE{M|a68D#j)?PaZb_z2OoIIsYF^=iyKF|G)oZ z%TDA7A!KHjkun-GGP3ucam*Z9=NQpCMrP$$l@tzToP&cyvPDr24i0gWd29~nocHh5 z=llByx`pTKIUd(_-7kuPNHd%8m(4D#uQ@kLN|k#dQ)^^p{15$IgWbpE|PE~zn!6!2@Y7(A{;Ld9)TJ@!X z$fh#TpJJgESBWm2IEG>t73fe4scu!>$xv;HNZV`%7SsIsO^R8i=oF=PGM0@a z+;Z$K@DA?n_J#ZIF8l=ydCSb7N7(0+w78Nn3Ow3ied4Hqki7G3*fMZAU?|)tt|uqm zeC*BSsiotF!hp^6ny~pfRK{w->DBZ9KpY){c%V0yQ*MBg zry?%je;@e10pO$TodCeKy8?glMiZi#Eee!at1*pBXwIZSmn)eRex6-=Z4!byJX{Iu z@KEGQyLv`aM>g0ebn9&-$?ger?L+3XA)8DLFZm%M)4LuMIDEu>W5A_+mS>FBrG;hu zOgk+SJ~~~~>Mo4AKY(Iwv33|RLjZ!$plne7PctH{9378s~a8JoZI3|XVZ_DNs^GaySdB7lw!MNq?Vex zd#1jY91I}B=pMhiZ;|4|@Mddob}gw--+GBideV9$Zy*#Q!uEDbWh~4We}}7cp7Z!8 zJSPv&{yKy@OdOuH29GbfJ##IeV-Jrk!b8J(Tx6N~c^ND{J%rsV{}1HZbn;PgKk-5R z9NE6OE;xt~AT457=vxsJ;yo--IanE`{JoE96QOYdaIE!D|8vTXtu@ceT(bR*tpo@c z`}hGDzmEzoz4Pl?vPbpvsXs$!zKRWubkA^!QZr0f=4LN@t&8tg$6%;zhqQs2Iwb~R zq;AO3&2i5l1Q2?^REM)_Zm>bsxz%NPZf{4q{VlXO4#L&anlv8i_OZAyFJ{A5=gYtB zO6YWYeJbdTwgRGRA4iZa^s$T%cMm+V>gMf6AZz}?j=S-EWonWbG+O}4dhJZ_oGOsy zhnww0et&P6NP6ohF0azeJ*Ay>e^0(E(!jp=SAW;^YgkbEu30c{4LIus&#OCJRLh{X~xoN}Ix!O;og zM|$>$c}uf7rYmb<15|xdYirXI-t8#gRwKLoXATyu$T!PKd8xxCAP17Sc~O6B@f&t} zaQ3==)zAdlg3V*yUNCwM(q51_axuq(b$W@o^Vu zD0yHVy?U9NW@mC~NS5_30xj^O0UG0F#PhIALj9NC&@HUyc;Z1vM~VH|*PkC+T0#q+ zI<*tyotzJd+cBT7S?zJth6SzJqe_v8rRA8xzEa9^e&?l&wWsa#fB&Kp!ea0-6h#l0 zOp{YGLQfgE)&?cHkO5-sR$9L5jLk|;2A)p`-seCr5#7k+p~Z0PpK7m!pd^Ouv>7wx zgWknFC6kp$ibQY^%ZJ+(k7^tWRe>hM&CcEf*DoauHJ|OZVb0_C0W|`k-{xtYoV{*? z-FX+&gDJu<=#RDW%7qBIzE{e=?F7Q;HuYLfF~!7NSZNa^bAzWD(&?83=3ry#vzI9I zj=65R4__swoHX97hiEpr=*^YlSG7+!3;K-s5c+4uzcq*|7`Y8VmjcRqCuUdxLvQPU zAhU5`uaTFre;{lCVt`TBir=Pzc1ktQjE9dbwh)ZSJFEZ0en}=5fmotFHFbQR{0g^# zKSrcPK1%t9=?FS4?=+1k)12sW6i1ZupWRamgUNacAyx!E$6iiN3*ksMiNyJcW{teL z!$eJ}%VGV=^v=&pwB;r18*cs6IAv*B=#9^8p_Hi&kdIzZ~0(FRBk^;+a&Mz2_W7`Rk4lJ(VHDPFz8`YA^kbzWV);K#e z_%P%Ja(P)gpZPZ;x_mYJ*t)L3m9&;U!;;Sfiy zLn!d)8*SJn;NK57f4PdSz7O2~_^lU)v=7%Ay3 zQ*>N$^G$Qj;_(I#d?|qQzI<=BXcj_%9Yb&{Led;|#j(*m!#8z|@hnNb0(vp5T{Vw0oCm;b;q#kRXwdt{n7{k&9V&(0RpY3>_H4v9LgHrQVuWk~SlBLFHj2+O!RMb* z=;T>Z{SJ0*9Ah~^X@7e5%E&T9W@NL~f_mEqTd*%*Gn;5w??aB$77n=f2gjkgZr5gk z15167@g^hkv$V42C$~vv$ZRnJ;3i6+HbQ~ahHh`ItELl&f($OlnEMG38ER(Rz^Y&KY0Yp`^4!W(~J0MF+Oaz)C@_RpbEy@^9+lUN@hKs5x@%%`! z!uTXNdZTV%f%z7r;rO+~S2CM(1QdXVK^1Hqzi<0R@!M5Zm3CFRbl!CN8swzSb3UQ- z>%*f+7j{vcbGc(B|4j+I|PTu!RRU)L6X+LHr4*7<%DbnQX6?IM_}ZjJp56p);w z)M2fGLb!1hPM7YHI25ak9(>HEcro&?W`UI%>vU)GxTue0Yr$|R_aGfY0s59Zf-T7* z#?b7q;a#WfMCI9d8zYlMmA6|Sb~0l*1J~7(;wv9`bjrV zp3s!kSOEhL8v8gHr~AjCq5G=X@JV2N1@^7AP!)}yO$Q-A+1sRcmNi$)3SoauAFOoL z9_%*YgD;yVDIxkqTEK4KHZnD`h2GDr-x+Fr0VoA-00&%h=*3P?A|cNGTkQ3{jkg!) zq(`JexTgp|S-~7kAS=(ab08^F$Y$nSQ9TQlOyZnTK;Te_C;V&JsOpbipvXoSo zW6~pD+r_f2hu)8WF#QkQA32!L5XqE7{Vc7@#S6+sECrMZmHG9`#a)~maO)TXh%_g) z7u(lVj>43&aeEz=facI7-uI4H3KMoc%DOww`xGYmFo%xsB61|05hh0e9u>d8agcan zyq_hY(Co9%0nKO7p=dpV_8Np2vU*FEeR3@}&w}SSlZS5J$OUCn&>v}3;_WTu(k(RF(d8Es`bwboc>rTJ)`OG&I z8PZy+^jWwh=-HzQ5zZTh>aT4V&b_SK7w|jxFz!n5H*M=zq|#5z>qYPrt)n|%OD-P0 z_T0{KOc1>SN z;hXe_v2u%Ix!c;)0xP|v1!HU} zR2n20r%C>s$a^88X}rCuK0a|8P8j%G*I?cHWY}n-Nv%zi;fZ92eZr0j_`Lf=wg<0t z-lI(i5(=^gHvPw*+e2LFvly4zu*t4%{nTrUi;D8VphO_h*}abCtG|-Fv*EQBk(7!H zEeiE-7Ih5hPE5*RE-hAlg`oyhUf!3Ue}8`YU#_ z7cI9K{&V~C{m`o0`pT^vhx;?sx+C%lIN{bf)$qgWYn3O`+qm_iz}JE#VkcP0CF^TU zeV1}Y)bE!~b*C+>2hKOl6CUT@EwbE-&L}P@h8AF2;ZNVT|EN0$#v$A8Y-L^UvH#+u zKHPlLYuY1{OBVEUYg4N|0A1IK%HBU0f-$ zY}tV{T^C%B77dh_dlFkFAkl8>I{q3mHJXa$sI zP)hfr+ROWP!Q)?kRda-YEO3sfu&D7eT;wLhusoW#i~NVE+^UGG-44NTYSv%8 zj#6M>!lwTSLjvzTL=G$zs1R(sP1_UPLKa1=&l5$wtB!)cw!p^%9w~en;#9h5zNo_M ziLwwrxc$6$A@Z>IWt$*JWTms?l`Eomi?tUE!d$qoxYslwZfpMs^4B|$ZzK!;b=H4+ zS@8*wY>hFipLj6zVamAV3w!fbMBhCh5QLGi!kqJqZ=stifa*f za>1y3F z(hgaIoU$g({-tj0P7$~ErFR~sO|dd!YvQ8{Jm;57a`N@IuIk zgzC#5Fe87^JdUAMy+kIt6B zP;i}!A{9keG-C3Nyoce@sw%|_&mz@k3_>1Afsp(IjZJW1wiIHQSDRxNK!*ooX=1f4 z_ZBAw4YWrVnqQ2FC?*dm7P;GDO=GJr)y~}_r)17-18cAbzY(i}FW8D<^jo%xtACpGY#j|? z$elr;+NrGxjg;$x82g6Pl3oP^n>Xr)vXio28Ef}yG)M9n_HzjT8^`Tacnb?F2;_#9 z0I~}?4jWdb^yGU=1SXi(okvJpIMhgE{3t5PKr|h=Y=@0*3Ix4xBD`4%!)+eg6+FEz z>ina(iV$wbBJ9bCI3M03AvQGwb8p$a&G_2H#k_;mRPyvnm;-I|k={M9PkW{@;n0Cr z&@~#e$&lQ8>g1i3(ee96qDRlM%W=Jw6}v~ z8FoALgdXRZeP>)EH#K`So9Tfx$RSd!`I;wEvUinvy{R4N;RfUvCPH3oj~a-Bvri3<DfIf#=(>W5Of_swOPO9IhjQSjtRGX=y1q7!Rc#%lyqhB-$$UIw~`C>N1E58{S1G z^cyps47F_|mjt<{3HB($F}!fj)c7ezyKk6DRV18!wh{Af;2QO9D37XYpvZmQ6;ubj zZhsPmGSZw{H+x(!M~r}JDo`nu z)UzWqM#iFI=VF^OIC0kN= z#MLpwr)H5kRXW!S?0k|_$H5es>ls7m3P7qYh1G=;9uQpO`~9Yk-Fgg`(xy4I6bv_Q zX~TS~gv{^YPujXV9&VT8W2XXvm3AzzXv(8nsolwfPB8>a-KQ>IDw+2)8PKsxCp{R4 zR6-6cVRI$+Yb^}A$m|$~X5?bN@Y4AeLXhswTwu1F3NdMq&8M%?rejW;X0p-3lMCpA z<>2M2G?A#r7o(h=1VlIs)AN{uxGYx7&?a$89>OxC@VQyDIbwZ(3RFi|Gp)SnZB!pFhTBW@R`Sy3K;3At ziFSy%nd5EnD7*ec66?&SHOx<3RY{AFb+c4e*Y;nO7VhHVi8$ZTexkBULyZpv4{OAq zF7UTD7S>H4F%fxrmW@r=kd)FEkP1A#{@0AvbA1ovp**om7+SJm9nY?OlWiL+3nmi7 z7nuVjBthAIf6S*=_ZoIK@Ov0ygj_cOZXK;9B;xnNB2|Yo)y+QkjcDr9422Si z6i1fpr~bAGOK*j@X7}d##CIRTt_XmjIiF538Dca5OYlb;GqDz6+?sF;p+JN7O?Ss> zpi{7&9$bqfy?qw@H8Yakt;eoe3}#M@#sq{UaUoJiI2ta5PG1}|od5(=cg%Rit@3B( z(gh2T#TCyREI_GL1cFCB3ad~w<%Gq?#R3z2QKhD^%GpEb!;P>@$`%cXZ2+DVGGjZ> zMedXKDPxKOF=aE@>zKGtU)Q@&a{%-j#Rt5q?>QOo++7LI5TYuBSmbh&86!yIMSqtRXiWu115%a+=Yd2iLvN4Of8z0cy>Wb>FFnsMjA5$d(3D7 zi^{gRkplBV-opX6EnXUQGODSKrnN^eMz1|=;IT8h$$x5H74+9B$(Ko@+N&cXeFmJ* zLJq1PBcSNP(Q~T@_taTkvAJ7W2eFl%1$@`vpMOvE+Sl=f2@*M;Hw9G!>)1?pojefVeKbhn!8S> zm`Gi-y)Li0WfU*%|HZju;hkfL=XRD!-NvfWY!hu8Oa#f8&^N+1gve%>OwkcB$M~Pr zjS}4xb)63tUp;1db!5$X*d-ClBn7=A(%~7AB7poPI;gkmygrAA733dZqUJ)|?rq*E z8;WWdYO!Vq@v(C@8T2z#-z~;^adCMQ#i9AABYIihE$xOfiIRm#lL27^Rui56ZWfM|Mz{P5S-^aFtVHA(_k4NH#usY$vKiV^{HLo$QTEI{ev2`Nq|&y&?6^86h%{l=<$qokzcAPwd3^uL(#?LioL(MT7uD7H$u;8_*v&> z|D0g2R&L`(>XQ86p17<%*XDW-5puXe3qCd)%`>f+;4o$iwAMrmCUH&qBxy9KI4*H< z<(m#*oi`5g$J7r0?Z|s>BJN@Pz&cu) zTv5?$O*o3mw`2ZyoEJMCQXH%KzI{&WWGECl^B!i+0IBE~wm$x6vYsfd8~Rw0*MzBr z1Lp`eqv1J)k=R^euJ4Cf!$}$FM_y} zAt+o?d)<^E=_%8jJBO0$RzZi}K8uN&rx8>i?)qhBIH6Ldk{^7S2H3QoxZesw2*2Io3T>O*G_< zD#9+6D^xb9TQX#cug0{vYH~mUd!eD1<$D>1<>_CX3ymHxku+8W~=e zTL2b8LnuWNy`X3Z(OE8BG1*FCC;N%EcZM%LpDDUAXD1wCbwbHBw zY779vvVG3kTg)lK$szz`(sYqIge_i<^O0wAUTOZTzx*i{w6=D5+s3XX2_Egk^74}` z$509DPb;3-wNF0MyOZG`v=|SO1qEiKyEILziaZCeTGlonyUgSV{)^1_HFLz`nrBX( z5TGf04|eHoO%&(ZV!U!BHmsxicvbI8mWUcAt~|svMv+hJtf&w2<)P-Ny>#B2gFS$l z#dZu&TMWMk5BNodmBNenc0Y4I8DM|jNL?2k;wRMWc&Z8{H|lC$Ew0g9`Dfc(;?37} zc~8TsteHbRZKYs~`8to8m%j_T$}2ruFkABVjY|4?>c*R#P(5-yT@&MWxucjAagK1R zk&AnBx)%CMFH|(NUnbEkYA2(GCDp=l?%>z^QVPXo+zr}HuCL!%urfXwaT@$Yi93T; zY%$>|e+)wKJ5?9Wl2+o$$8eXBnwfl*keCM~_Q3``j&Qh-M%KTn;?aw~%O|wN6Fy(J z&9}ZRY0}M*oZT0aD;^<-NsNvGjtTN%b%<;Z81yw%U)8&rFfOL0xt{Qn!V0^=E}2Ki z&BMD};xgk^Jl2k(G#QC{<~K<*53`*MaGmMqsQ>N25-<7uV5la1`cb;4Jekj;}8oq?kW<0&Z&tP{A@ckdTcV zb4w_t=;KHCp8S538xsad%Y@R(Rg||~`UN|5YsLUImJ_MUU|PV84DFIgZT7kRML`|T zjj9FAww7xRGCMy2+70{UjGTzzz|0*T(IkD*0&hWkU=ag!#m}gmABrk2^sCYIlBF&i zS@|w2C?bTRUQ2f=WjRn^jybE<#9K}y(s~TP0a)Y1itZnrN3V4=Gt~P?8Z4F67%nBJ zyH&CYuc`)Wkn%zRf?qy*zaiKzK4vX`&RY5|^n<&)NdgxiGNJ5KSD)3pjgZBXz$-SE zMwqn+_|e&{{poL}R?ki;ATQ)igGG0nlZ;7+;WvH4W&l?t+km)7G0p+*cAd4+tKQT% zT}eQLkqIX*$(+uimU@`AeKEqI;_TdOWx^V9oesmadS6wjoe9Zim6eis1Jvo+3c3}w zj55SZegJ~!D=>>&l>xyX!E0eZR{qj0tyL?MWSD@=4a<`*u)Xe)S4z(v@8&OOzA>B4 zO%Pm0M}SfA#2(D;UG;g#N1Q28$Oy@v4Osj_;&C^>l;-ay{X8QwXnqCqAb!>*5g(l3 zArs7>xAgRkjNQ|n!H>*`TpZD&JUphf@m#~I6#JMMcMlqt-l{Ier))6Y&$&5ItrB2! zcZmSSxJR1tn{XwI0*Aqu=>in+%$g&R@#S3KtHbowx`^5AFoQ2iMm((S%d*!1uo!m; zDztRTz=s`S##FpUmC9VNje3z?b*5G%Vsxg=-pXR=m=F&LmBJTPIZlnN^QA(^?#x*m zF>svXGL{PG!D75$yF-ocN9F`o%l@ivA_P`zp^;b*D+xUwaHz=37O7xu?dhu#DHn96 zD+>r|Z+GGj2zC1_zg__Uv(kN9(5lgo{^}g!L$AmwLT1GlyUwC+q2$Z?7mZ_=m+Hf6 zPeQ2s`E=3_rnNg%KoRZu@{n=3>#5C-0(TO3PZUx}{6Qw?cj?@W5)|=v;$>n5`H(_1 z{uP+nO-h$2eFZ1Ij`hG zRGEGCB2^ieMNdDwyiRH_{$p2FY$U$8PQiXD|DoR<*O%*g0H?Na1np zaB`_;=_Sp-PTZgnx5thtiC;4EYFlyL=I3kah3=LcXQZXolT2o60Bq_az}Hjd>1^p* zc(xGL_zZa4;*jKYAlC=1D=B;U{kF>u@X$l<1-4p zti&=D&f0em1I62S+BEZlI5aZ|vEGK0v0UKmCu{I4q_yR`-#7HCT4HjOFh2KL7We%DEWKgF7etTY=aiCP5meQr<6KB+-S+V&T$TE z-@d;A>^HHO1~pB|ZqY%|MBvHMj*I*9amFEMv7E)^Qj1gabpxxYq^rvj)|HX+@_Lw# z!k_~np*0sgh*wfjkY_IBvd!)jUKF$CH}M-_a422Wb$Y3+YI-;~(UG<3rbml8G`a94 z#g=adQDWj9<7c=S4t(w5px(2|a`*A(n~hXu;3ggmlY8YDKw6S6ie#Pr`hnpUf#Ioi z!XrMz5ewGuqb_yDz-F|oEFA(x@(6MDJbRH8H=k*As&@<5y{kHF7!HNCo_(89q%?#D zDLvHIjlfDEsmUgyoA)v;{t#~G{#ZZ6RGs{Im*2htAPbmcrc^9j`+8c#I)jN%aZQlE z4f$=C+XZud^O>(cQq^fM+qB|!O)eJGnzIMC?<{Iogn6M{{~i{?F1I~4`3@6zA+8Ld zjyw#%H$lwbr!Rip@Cn)tA6WHnPKllHS-(HtwJ;%c@yjT;5$30Y`Oo7ypBs(i#n&v< z(@Y&r;$=?V$T(N{@yb)A+ZqqImI!dZlH)x7z8_Dby1_OGy#$Y+=Vz`3neYZ4ZG%RWqAys4wbUqU4%Q zqV8&22Q7|X5T`xl|Dxgg)SXO|FZDONxz8cq@INXzV{-bMuDFs|^*OJGM#~OM;jyQ6B{W9j; z;SOcZ+pSXiv+MTm>1@MAS&7B)FA$bOWM)vVbWLN^u`YIx?oqP6H`$Q?>O8h;c;ya5DHg|B_#L}h{OCQ5@5(F??z3u;jI7>V2 zw}Uvu9DeIP_w(EZqP>dO>W31=(y(7~kj9o*byBSv=i@mGUpq26&aK*$DR+do+QQw( zpQztgEi2b@&@4B7Ui8tVuaGca!7X#ot!L+CZuj-G#&Z3h1e1BwhaWrS2S1EeKJV_T znSLZ0i0^Wqn<&;E)T%Z$*Ok%H9W?3D`BJx3NfjtO@aj0CY`K&BVBBNMDs;I7&F@!& z_A)dF%vMX(wxHFayicft(rPc=7j0}KYL5K*`qroO+ke&ANMEfO#Oz|8AjNHTfK)SD zovL10G#?aclN0T`x}dmYiaOW$(dOgT-M4}pTiY4(ql9r=f^C(#8AT{l+kBhn499Av zQn$>K^H8uy@GhDyhk-tRk&+{FGYNWnJlt1p#()SzEAM`j<>g(imGh0~h z$~uGDV4Ii2-7ihSvCze4A2M~mew@~%TlY*)ZyTTVC-*DwZxQmDz^f@!E6zrrqr++* zx=-WXf2?07Y^Wc8wTDi(ZGpV+b>Ft~<@{LpjZ7i&)aXL}mUH5F@|Um4CZm$(GcWf2*5D9z$cKNN>gCVm z*FZh*^nW0eRub^q;q8qFpMLGEn;z3G6&h);`+mH&K8^eKQRqyk@ z_19M$iVRiR!s+z<=v|~L{;mn&p0EC5yizmJ%xtu$dQMxGI7}zDqcpX z^Q2^?6Nx`~uMY1d)O{6a(xBe&4S=ynK-|T;(G?uI3n7VDp7n3@r3F@zPg&fnOX?b_ zN;C2L^eHc=2x&RptIdW;9SGUrct^^c_laOIO6}0xv0WgB$`9vG=Z% zldjgT{VFQDg9ats(6Ks`g+zw-f@!2L=Tpl;NXQa01@+n|!Wl5*U#XpTswUj7%kIfa z;kAmwG%WdCKgHp~95pav*jZLOG)_N1F^(Vd!m5Mi#j%s;uAyfKaavN$KGzR5*(}ry z_8}|5IAE8@54jgyIMrkrk%|q}qfiX3Pf5u{E^S4PLqNY?x5BUMZIXBJEozU@zQS(Z z3aJsgS%)tJprA|5bru$JRvs5HnjfbRHkPL0X{C#sWr*sGLCtIiJ5tJ$JZLF&pi7#O z0L{$~FXy1;63A<~Rf`pj^y%yvej_FYKWIIF8-#7uK$=B>dlk5S){SnSpn?V4DkM#~ zzSa%eY?6d>$8*9R{ zbfbFLH^;(cU|_l2Ci_Nm@uS5cnWqf58K*XOcQ+1IR=-p+UagYn9NLg+rttPdF(KL~ zr7^*t`M~TV3^3h>1t)X|Ykt2OD&nC3Fw`4b?#lNN#P&A^1nLyv)_gs7LV;yfMjyK7 zeYEceb;D+SbfM)s{nIi0?{Z77D(gQ1cFm)(w$PIf6nT7%reEFumS=|aTv- z$kt8J(2IVs)U_0tm37110L^(QvBFBFwdg!&bZ!GUlEOy#U!fbMk zyZX6!9DV-i^j$Y%Tt;TUZw*A@=h|CWYfzXN;*FNy=#J7Xx>+UN{Kz#p$>PU_H^~U& zNUcO#rI=c7W8GZ;EJoN%w;K3)a;P0!d9h6^p+s6ewe;`pY;{IzO-vYbQn;bG);Ap6 zwFgYiYyu~6`yF6{9`8Ch;0ALJIV7V{)hFp6hZ4#kM-7}*DvM^HV&}PI#JnL4{$FJo z&M@&Es-0%tQ9RpW`6GDf>Ids`1Lyz<_%U^dmKGTzwOHL|)91LEg53|`Te>U3#0{91 zXb&gDILTW_@%!Gjv)7Ob@6JPOBLg{C8+YL(z>UxzqUH)D?)0K^crVS}2G1h%Ib6a= z!r1|f^?Ts_+uV04GBVj{UBUgWJ33yKVubA`rx-7DT^QtNtP@FEtc{G5XNyF%w8l{N z?va;|W*&>V5f_Mm$PMk32W#Su%Ek*eFm*zQTi=^yHjycV`TTq2tk;OS!nH7ql46gX z;MyV+p+D0O%vC;-^$QlNLl{LYhwbs1PSELp%9KaZWkgtzEFQB=RvDnD-jrA3RLHIx z8h~_eO&N6pmE67=xNu(%Ho1?w>*Ht)zG9-f;skiYVVcL!8wlHs*tu>A&nQC|l{o$b zF*20U*nNQELfv;bBRM9N04LOuaCyaP?rchZ-2I7N;YO^M!v-R!W*+vjNo+V31?gVg ziwDH9lfjO{9o#?K5ElgDqA$l~yNsZhiRbYPfkJhdUld3XOt7f408eD-`0vvw04k8s z8mHenN`gaA3(tXb^p2S4{hS2SdW{p(R!>|M4B5a{bX;w3#2?@zq1DpkJupPCqH-&f zA$pqqb&0V3Pv4ixMh=0Lh8DX!x{h3| zAs`n;th8Syh@%(ieLRA%e(hM%ja{LsYjiFL*)uWRvC~m1f2tF58ry8wyO1)-rqHwz zorodA$KNAomHW=laHP!ua$k4QVl4x!LG!3gLuS-r;F-f()X-WC{5pPq1AUB`Yq^US zJDMH;!(lw~P$Aa7)83ksr_3r??qcKrcntqAO{@zkq;wv7%iyO~1G`%seVfS8e%Am} zS*AVtEd_8%j&3&ZCA`3Tv5)DoCVk_jE0vkaIZK+v-jslG@r z7irc7urHTrzE^k3P>!&$fz%7^5-8DpZK#I_9nIwiM!maoCIQ zc})ZdEA8)bornG=y|nE2^E?i)XW6~{dAVh9CnecVLNh++f7yHD&Pfk=suD)izvMvA zJ*3{cmFDe~A{2tqmeud>6doWT07Ocyp6h2%3-jxnt+W#rfM|^Fh>J}3G-024diptE z<@EvA(=Sp?w=~h@Hp8+)#ip=^Wy;F?+0v2juU|ige_f(d6DlLX%2ZW}kGfk?NS$dG zmbtCg4H^WZZ;|D7&gS!g(a0%~C}1>V%4XtiyBZ?Q24yH4Ay5GM;6G5!y`>k;_L5Gv zpF#S=Mp^>0h&}-1Xv8CZ-JE#}vy@MR3;gPcS#1Q51NG4&*BzK&P$l@xK&pMh?h(zt zpZ_<&2^PL26GJ|gk|0(J70nOlxluWQ71Mu`InT9_e4F4gK-c&G50o_;YuX@x;coguQSHP= zexHt&8ZHXNt|P_b$m2^^k#5>`A>RJ+Ah-hm^=@vp!ai)_RbJ98%v;;5?^_Bno&+>V zumE_CS^D;$1U(V9Kh_M>4B*u=NC>M}2`h<`v?%buUqC6*$E5mpEZiMR!Ci_L^=CWu zO`LliFE6e9JEEJrM%BVtyOT{Hz|*?G-3~5tItw?Q}g_*k$QGB;UcoE$rdW{Q6A=(>j@MzU=NFVJ`E}J1e)CM`tC4+28|b)`CWeU zL?&zEYTVX0#_K%3#i~53`EF_l9sCw=SHN?+wM;~nBeq#EA0Zy*0Phi`CL*&5nC zA#c8!1)0b)G&6xntyQ~?DMPu8?FZ5vKC)a~m7n--A+0~fu>HjAqY!903?ZpuO1Ic9V6E^I%)UT#FSl_F6 zicve}3T29k9t!xw!3rwNSEe7{8MpgIUcnJR5U(Ks8`SF8;_5$qwk(@Mh08(yo)&@z zWuYc@k8vJ-w=hI18e!w=_4K4uZid2LAgjc-mClxV=|Ea7d^dvKS#O8ud~y|(KaWQeFIGOz4c6HnHU*qAZ5L+8DT!wla% z{z)l)4L>u%nQu+`BSKI<1fQxQOT^%h0G#DNxiDQluLV_6SZYSYhlQz-ojzLl-)k}w z5HD32?i=pyH0;Cj8TbB~Ph0@bbpVpMXs*(*23YL$EypY~t-qx~yUPI$`}!B4L9u&s zCw7FPEDbuBA#=1(z)*x1@;1E}q{7=6hfTVAH4Y=8*&s@+*Ndb^L18C?!+U%^am=F- zwb|mgn?DfHeIBc7Xg2`zY2A?69)&w|{=qhw66*#P02gg?K#L(sBJ0(VLdQnu638^U zKZHeJ^}>yAU6Y8$4+>fDf!4+$^2xIogE7f1>5Gl?qOqQdi=X_AcxFPGH5WSv%AQmk zIUk@4IM{q#BwF6FR6l-bA(|5|FRt((cjQM8m|fTWrW?;T}1XpktHGI#0;rn(FV=?w9M# z{UjPXX*vAviZ~E~CmlYEY|FKVOyh$?SHddyD{7q|wWwt;o!KdL&FC)fYS?8w<^48Z z|D%qsq0YeOTnBE8h<{b%m~Wl+C)8RiO@UX&_})yC23DtkMsxl((;OO8PmYg!K&4%Z zxcS&VAz>lO?ovynk-ARbp!(g6mZzPwsn7HP5Vc#1CYJDL4_8r3*taKAZ>Up;Z;+)M zVp9QbMzwkh$C9UUl`#kVjP0|?1Yi%2+4oC4Njxyz1k#$)g_eDr!gjf@G8z>qHWxZvRGHPjv*d)x$v?g{526?K)F&hv2c*Vu&nf7t>HTua=Hy19>e zX+#~I1+XgS@c<83B?H@^?fbcRhOfrX81PR-_BcvP@ELlm#`uBXQ;P6#U1DwZ58WXA zYx=>yjPLl^vC8$3QW9~y(G}gLrPgb@?kx<5rWuGoINj#!xu`wA7#%q6(%$wT=q7Vf z48DB>Pxh!Qn}Q_yfqdIui7ur7;y{!esTEIg8GD)x$X?F1@4(|dR$$Hm8&vuA?7oNo zw{_jYz&s!))qJWs03mN>+{bll7bq1SLN*+RPGd^ifmP)-H}JkC4E-Odcy1O_O?L=3 z74~7h>_4i^s&3FVa#NYz^J>+Z#(l7MIZ&!nxlDt#a{(qmAkDWwQ=wkRk{qEp|A9M6 zla#s97DkETWAi8P0yZ7AU8-M!LcW9Agwc~4cl5Jo4HRf~n#YTAFG zw;p(i>unm`neGwm?5B8wR&rs=6#eA7AwxH~!vgab<#Aw6>9Wp+73=EjmEz;kb`Ood zY+N+a4#>#jr2W28JB%!!)=2WW)N+6(#DtQAJKm6T@DKLPFvXaY6DOJK`~oyrd7sfz zaoL$PpkK&)ik+2x+RnmQ`t;4*cq|oltW1Yz^iC92F5ysm8)0K#Jp!_`%Xr&Nys(AR zk-S_z4o^BeWlX>lW75VcZ}v%c$+J0Cr@ExHnQKAU*?m%x8^Gh`UrVRo#<|c-?xTr~ zK%Tm(W6q^|^0i#ji&r3+pe9-FZbDk$%|B`+JMG@_+<`Lut4a}%g5!$HxplxSu?PL$ z!5p%xdG*2VkpD;1cL!4afB#>5OQNnQdiM`}4Oy?!B+q^LfsBoX5x&^L8(M%5ShWbR)DR2UH16 zTlUl!L7mw+Q$&4!Ufu>OICS0tl*?~=JV+_wZzbv7K)l`!Hlq2NHu&E*_gn6p_Y6sL z5Bp*UC=p_hUHr^{bv$1c_^4_1$a`U<`xW*UAW?4>E52*LbtGE?HKI-l_MPvg>C(Ev z;$EnP;XRkooYv2tZh&?-zT1fu+(PE%@k4x?E8irsTJ4%xb(6==vGRqvS{9b%^Xp3j zzz0>i;s89fOsmZBHQw?9LY3a1@s3hDp-NF zu|_ca4tt6(NLOo~MJlU-dXl0iaG4QiXg$$|tOMfDNji4Hy!p~_=G&{{s;WI~14~y8 zdN3^agf27Z2(-Lj^D!Nai5o`$xHYm1>%fPn8OclKJzSKqV1lIL0oSILl>cJMwx(R? zNabs_wLgycaZ}?)FaET}yv}qA>b>_6Lc+I8at?s}=i2!XedA^4-a&uj8}aJlyr15J8D4;Gkvu;bVpCf|nmKPi}nVW~4E$2NG` zlh8x=x82s8<$;kzU?j`P7s={Tg}5xPHu%VfN{c;g-8u`~r+9z4oC)=D=X`?T)D)oQ zTyvCJTuf&Y;wWWwKi)ZlLXr4iV}<4O#@%q_f&aUWwXSjlQb!RLM4PKUI;e_D?pTk5XnWPS-9L1ob?2ca zl@$9$R+qv|w&EiUqXIG{@N-#N0z6<8!TO6{#LphJe^x(_f)|!~)3`sQW921*2KYrz z-)HrX?YucZ7TO?zh!90tB1ex81O<`=Jh*TgkU*2ueu52+uC7*J6wQ~6zdR1Ds_S+0 z=;OvMpk!rMEhP+Uw)sE5!HkEo&?eTkb^wq~J%akmm#6BU-#1$iC0j2( z;XtK)3=Udre@*tsUBlQ>7e7Ixj?7qElhd~{vj zTdnn1!Bh*i8BMjG`ndXBWL4#B!^mQBl7@Vbbguc{g|t4VCztukhV|3*L zU`=>@?Pj(Qx0VGsaPUrC#4c}BV$C`SVC5p@mRR_%rGDz;cvF~hTXGMhR1D@W<4fg?a@-*?n4?%cuTcep&ke~|08-A-Z9%x z7THeF1AJ}wPG`bI@0cP?sS&?Ae^+k^TxWR^+wjR*%ggLdc}3j%V?Z&C_+3)zrw2ZB z+icTzN?0&T8+!kSL6Qx?5x5i#&Imxq1u*=pvmP7QprgJ$>TGH4fO>7#f458L;-~j| zEGPQ%hEg4~nVmkUsE>(nB=avJ;S8xzCQUp$)u`M5VHudMBA#AP>2{HKL^mk*qvYc6>{U8w(Z3SdHgAtMtkPLAGrF|OdbJH z+D5FQ?HtapB?dKmyfeYc878lj$v2w)>!~koWCOJ0$@Y^1=~Im@fM(58BGaK(SqUA1 z=Hm_@TFcOY-E)OO53-9{lgOr!WKh*kW{{S7GZu$gs38ITxN~i*tZmG60_PjP(4P`@ zmjms-3^M4M8vcEH)RFbnr}H=s#66!#q?BzN!Z4Se=D{9)k1AUmQN5gdnl+q0_J*%)t-|on6WX9IdE@r-yxk0hG(6v~XONk&l(a>G_p9RKq%IM=g z0PuZBYRu?G7Z6mJ%K$W9-5oG$+9xG0|uH=w{E5;eh_h zFugieHTYQ}5p>ZDV{j*b>*rqHT-)j^o2I6D)B>(@i!`(TSSf9$?xgng>DE+$+Q`}V z#ONr4+6xuTca4`h?hmo;ch7wPWTl%spJm{IQ|S8r5P>_knVQ-|80XO^xwWlquZb7LiNcSWx@_M@`zshLLi4WjRI$(;{7|0W(BXoN0lKAV>?60=h z9y#Ofj%=1?pW!%fAq9?rOlAYq!|Q49Z$G+BPA1-pk~Y3z`0F#(dFHm!Mq_zCO5VOX zpM{EFhQv*yQp+ySrGZgp1ceRH_K^D_-bPF5{BydzA{IZYmB{w%88;}O*Qi;`NY3dc zjFbJF$9yRw9B<9*p)961D*4>1W*)$&Kbd@vtvB?4((Ls*=g~JGi^qjtqtsK4?TJ5Z z%g_{cjo4q%7Nv?mm&wbr3F_CvWk#Kjpi40#I({x_qAplWiXXq3Yh6suX*E9 ze0#TBjPH_Gc*)`zXRGV;;;TGC=TsIci@Q00z@HW_q>Ej;xBMmh<9k(cH=*huDnP~f z5?EN}A!vA&`-dENkxDpf(S%NUe0R1}t-`rDs<*9x&rjJD^I;Low=55?e7Rl=-D-n+ zflmbfSv!TV6x$i@hW>#E3M2-ln2(;z&l4bykDlfND9IG{R~+2FX$Oc~JAcs|Oj5T5 z#Z06I0ucH*cvxW9l9dih+B%JMdd88zHUy!|>eY~>cF}TzG=zmX=B{AQ0TzSErJpRI z-?e$89A)cziyj|i55X6T6~b?D(8S4f#XRlmh)t|FeUt3F)Fv8l4hw#gmEDrWAXcx! zp-IHHcOJ(ob)3{sj-k0}lYp*APAMC{J^-i;R=IAw2)JabmUkR)Yy^}*H+)q`$BpYs zVmC*RBP(2AS*G-Gq`D??3zX!04@%B$z~eCjD>3p?J~4!lQ-H$YP&$%4>Mpd0REx>@ zNc~fI=rsF(faZeK-COpQS`CeFG34`w;=-*-@H|X7Ya}gBP9&kX??R?!LK|ObzOX{B;p zYmbkek9`1Ry^6|*sLr3}3O4JiVCz-D+NEm@=a^M|`}`03O$3g3hOGRRv#QXTooit^ zoKNp3T^N+vcJkKm$52oKjnj_oJ0Pqp7PT>DrZU%@!U6G156x7;=nKcY!e|6_issBj z`9BsH)Sw-lkE89kI}Tdb4qd;3DjE$$1eGGp6Tc6hw2OdOcY_M9eh-Z9-kTYl2}Rc1 z)D?xC?aXyV3xE7g`Tw6U~y{|fN#kZOUuY)n76lAXy?gUtb?;O z{f-qyCn0ZakzZ^F2Cjk>>q)2U>CWRyR)8zmM$sOPfFB?M%D;q#{x}%ouF`kifYQL^ z-U}w7#jeBP#+^k!E}I_})=&6-TnKF`fl48wzH2g@PXnP^2EJF?oDlJ|+O(?RK}dGO zKkp2;V)I0U5kAr(2>43t*}Ki{yP5u;(y;1kTKt-D;N5|RD_ZpDVWsY@!=a`j-341D zN_`1N+wr9PsTHR~U_@wImd`+rkN!Z#AhW6<~3Vw5}+CTxjIwxEd`DcPtFbgD~ zA$*vkk5A1ak$7{CQ?Jx^q<+R8_&JZmE4B|P^?L0>`Dd!_t2?vYOFEU~fkylyfFGqFR`>ElK zx)(a4pDWp^AL`l)LuxpK%dfO)$mj;YH?d9U09rL*bI4HvDLMqUCwRCiV%$wM=937M zh@ki|X)c|}=FbkUVwe(?QyZJgtZHtKI@}_&Kyz_c)ksV;X=?@>^$$CA-J)uYO@cnU z>;~_f3q2}hG(%rgUA+^{{YRZofb6@NuZrS!j0+ctg^6R#s=_ZoUU%eIqqpZk(mH+% zlAAM#jo(E@IPBJ^?GHN(r*X=t0BOSH-Xir@P_Pgj3lqlQOG#H-r|heU!UjAZ@`#8(^qWfS4It14@=%#WE~?tGN4|}QJ$;xVoWf!ip@bkU4la= zfO{IXYM*WDAX+5?iCL#OApo?O@GEQ`|!DhcNtW+%Wktf zCep#RFq7U(l2dVAVM!O9$jkwA0QYiahND~a{{yAhO)Ki7DBJMR(>N{56b1*ehwA`0 ziwVN*C;`ceFU!dBic@=Z6Ax?jnFN@FANI)kt9KM?t_tu#mxUJN%b#Q=vW#hB$@wBd z^Cug7+g=ZM{BQxXvc2=lzmS;sG6rXb089s!y*YZ?BgOm$<-F7GpcITO0nogL<- zVoyVZn%3Xirh|8Fr1rQ?Rf#HKN2MplVRS{mE`JN&in)B&vYxv?MLiqh)15@W0Q(g? z>;1?g`OV8!u0s}O89M%|6k4fg%RIOIhLy!)?sQ2b9a{G8N5ksQYxc=-+O=Ml?*aMQ z2**PL=syWc4BI*J2ly4gCY~V{qlP*41(4qyS!V5t<(`S5uib_R|-bl!s|wLq4k)g;Y#rcpjX z6AwQKczdZp4sDaE)Ex$51}V}iyPWS8<8Li$bJI@BvBEZ6Y}VH0VR8HLqMsk`F|AX` zi~ib{LGdqcr^lag1)AuBfhl8)B%4bL-inHJ*t0pyZ57#LGRBh39Wp6*2(F2jn1tBf z6D`@zv|oog&Jf|UdqY1s1wY5gEr+QqNTo&y5K0hbrqx2IwShf= zy{3MTp!f~@_-KThH{K^w`pgh}B5}WNL|KmS-l$iY8k+rc@M@;qxBPIISqA;mzPT>w zEf^}lr3GDaaLU~j96Ztox#;QiIX0#pa=LyeQ{Vk3mjRBnUwUP~h5T1N5LhoSX&c?6 zgnOGF2qy4JeXWunr0L}t68fa7G)*RuPKZ;ueT?ZHb!E8)+e|AA^B~zkD0hrm1qToE z90LLGP4~SjrK0*MAba@`)r1-uax+a~{?UBM2dH5dUHm2Ak0eaMV|Mjj0fajank8Q) z)$O0*ap<(;vxUEO^yFUjx@4&$7noyCa+_wfEKt6Amzk2VxIzOAiM0sik#gHKhSJ{t z6kX{@5d0Q1L(cWoK+Y>G<_Wb6rJ=)z!764-@jQ6c5iqF!HIOqYalm%h#tQO;BF9Hi z+4Liyh8np@bbGf+mI_N&wlrT=5GbWd!VS+G&EnZU-|e z`_gjbYd#=&X^WUYh{;cx)Ji@^)1RnCCWga;8zcTNOxL@C9zi7?y%1eY^ zR#m!iwQd#olL9Vo-~}e9NkkkHT9MLmg4*}8k{DFn_x!GUOU@1u5n%!X4X$lh-#pl( z4K}uWgaQPxVYr9z1vGaAo};ZPz`}rniI<}RMGt1=gBD$sK|fWh^d3?t!w_2)YeyR0 z3)>bZSLSp$`9-9g{7uP>Y`H|hLN{2IF+rDOcwzH&$H(CVIm97gBTC(+iCv-1rBZTk zTqB&_19VQ*HoX!?<5V$;^G5;*8%6L(&I)A8xcgl!e=9;t)|&W=pl(?mUm}?rpen?z z#K7O+km}!arl4jHpuBMM>5h2|5xaxmYV9C|%`Bu(q&gPY|K#}0#Bb*H(2Uk9fk~7Z zgHw#I{7-fs$tsV8=4BjY;7Qh+uFS%+6Ku43AJ8eFI{DjZeDtl$byOV|9ZZFrVhFmhB8$@*+LP!T0AVU z@ju?ue$iycoO3hTH5gK52p^--5a-ZDUK3HxDi*R?m+a@Izk<+&aBE5pG9{MLynx69 zFCnm&4WIJyLaqHip!g#;+!O=#7YwA~4=+6@?b@Xy zpp60$bqigQL>-<_+BGujTLfUAbOg*%-L6GF(WfkfH+nf|CLZC8yz4`{5S#ykROU|1 z%>r3yUQ2AIC6@D!VPO(rO2a&G{8!iS+@@0S+Eho9tlciZ{Ux-8{tk?)P-jfPpcLAU9QtuThAIeX41^h^+kVH z;EWi<$lcGEo{StBJB)My*irWXf?n@5nTNfp&WhLcRBI%(x#qr;XkuuE^<|Op5ME{g z82ZcXYmt&__xs=+>pwmU9Xrg4`ufR@Et3fx)TIrZWB`c=YdQx%tG}YCCHb)+YJO{N zi#UQ)^UrPd`gi86!(7l^-#nYOTtvZOx1R_M4DPR?lh}P5w$KC*>&wanwCn;cWY>Q)1x;xEH0ezmP47yKVGD`;br=}J#KggGr zdhJw4hGu;HGu55^;w_y*W*~^OKeJP@P@t7j=L+IU8;OPwU^oFg4h#fl><|kqCt6UR z`1k&o9Jzvl7>~eEgr}_xr)^>BNgKLb;R)%#C#_>LzZLcSB)a)D#tL@*Wqwou1uoZ@ zN_2cbe_J3`hVQUpC0@SZt=?PjDg5!^-bwfLK&6z8O;h&?E=KzJ7;0&s%y;JZ=b{TZ z-;<8%(SyD#?;H9aV%Yam*L;96wt}MicuPXMFyLeB>7Cj*WE-PUvRi0&^J=mcLnJ5N z8nfy{KCuK;@PD7>A&Z0>uJ*8NDz+$YiQ-#Wr+ISTPE$rfD{U+!@6V{ktoTztV3%V9C;DUVJ@ZRT`_;w zd3y6y)cXly_^w*lnV*zBnnazh;}DUsU=|{2ybcn|O3H?T`e3E98c`rCDK)iNBx>n* zt;_4q!bs6d>aF@!_?$ht+0#2A*iueD*^H399l=EJQZ%B_!=-6nEMegEZDA(tfMj3e z7mF-V%q0`C`A+Il|AH3PV+L*}v`TR+Gde2KHkcOjBEmOyCTOckD?bojlI?6dzmC; zCKxQchoKP?%=QNfF=FHY)>gpG1brdEArd$YH9U^bg}h=H47me!f{?H`qttSODju)d0ZYS( zXet`hkX2<(35~xH|2dHAYLl8UP6pQfRcwh<*QaYQNn12}M-xxH(IktPT9rP)%Uf}{ z2irb>ZHMyFhGHWm<-b_emzpd@my}joD3Aicwv;$~F3r4}W>t)iV!mT)tHBBcM(=xj z*I@Mg@J+hJ$ax}w-F1bWbfESA=)YJIAtxmgOZ@|NV?9+c+CZ!n;JeK`#Gs`Wlix*% zeJ9p}i~ScA@?26be~o?Q$FGv?M_=of(HU>Rh2ld4Pmna0{KmH`sps|%khq|eSQo{j z+4(M#Q=iw`xJZ0WJe><31dz0VIbt9ei>#m8gWlySSA$|BtCRDQaHWA1B`A(p&# zs?m-_Y9%v=GTXRQEBJaq@rIv7P1zHYWP+KbQZGKx%o`gaqT_4;sUrhMeqGMPjWrp} z3Q}R!)uA`*m*N4XSMsPmMHa(PTA{%w%?jzmc2B}kXM#@Mv56~zuG#I}3e1>_OaQ3n zqt}n1dp@uo+yhkh|7N1??Ph;6y6zp&saoHXQwd6~WBgvnv>>Ex5yl`S?+=z>3i$r_ zqB&=F_;UE36nq*XI}GT40vyI>Ag}JRB_d2E?Q9cY!wdw)#UQI7<>UUZ;)lkoVD>|VM@W7u+e_S?kAjFR#%e`U8( zN9Y7|`$E(>!)nAm#n@mK4FW%P(uxb9A$!i<_EE^YFWFRN5LyuMSs0j~lCiEKWTuK9 zATrU|eFBbrW_yHZNI7su@}0Z(30|KjL}=v(8J>?MI1q!i0Q6dSXDuzyX@Q31kTAqCC?*9Mw3;Yo{Ui%9x{Nvgkg9RO12%-QSEHQo|Q>k&q@qtVKwp>)d?B4EOoF)ty4P@TU*r&C>^_5-uck#G_++g@4J98Tu=FG7Cc?C z-S#g@@0j{rEY`M*f2TeV5xKMWaV<1dXGp$-L1i2;&XSU#QyHeuMhQq#OShN8I!IIa zdgZk^u}aC|;P)>v=y+#My6jB4##Tp7np6&xL==d^GZFPH@JG2LIA269NOh1AxHmk)eTfrUhO*FW3@#tyM)RX5B>xhVBNeuo6{nK-YR8tTd|1yFesx!BZb-HuTt- zaD8Qmqr6^^)=DH^pP>yev>M%$M`_@+{l8McF(4ZfR|H)5?RL3Z>=iHZ5~~T8`O6PksLP~W%Oxs0v!G6U1S^sfL+?^z`4<0`b1;;L8lu#xR5QsD<$tv zUf5a19*Z;Mvxev!Aaa~tjBJe=!6 z!74DZ+dieBeWLqU)Wn%b{sr;oJNeEK;v}i>NE3OPnel1g8bO$v)C);%(f!3VJ@@le zRQXC@+8_JHL`5SWBS-&v2@*F<9dn$V&O zh&@4^F0uIdJ3zyt7I3KiC2xJ!^gtF$%H+AapSCWm;{00H^If`HYVoPR-gWwU`Lwq9 ztaf$lnK=<$zSJiLrBXQ)%Ha!dZ z)=rbu3gkEn2<5ALf5lpVE|KDum7~_^P>83Y>|ask5%me#IQ1c&K!e*2;2n)X(^IRb zC$2iY)3Ejjwa)bGlX_~!lcd7<{}v?Oi)OFFpwdeF(`WkO*?~)~O?i^NXIAq(T>54_ zM7+kgY(&6KzA+OipN(on=r$2$YC@>xh8Qrbr4zCi8{tASISG`$uraM;V6A!4eO_ zz8&2@uX|@IhM)C~o-=7Nw0b;MsWMP^?dcK&r>C`PNeS%Tp?R*qzR_Mik3#hkk?)(k zoT{fI>Pk+1fDv6v|Ke_2;l0bP$&V)LGx}W{nhR39%CYvl&B8Lyk*^9xk23z4_SskV zXS|d-uH^im z1#Z67aH#aRm4=9`{O4bs^$t}W80PHC4=lRAJdEfLu{OH&fa8tZ#>p)sI=iKYaQo_7 zUmN^6>iLqTk7>JQXRJ#n9^oQO>A7X$Q9Z>Rr{%IwOU@nH4@6~qmHoCj;^N!Q+8Z@nC< zvHU*mB`F_0U6%4)Vrl7ix`cN0YdsZ_lQ27$I|)DCi<7^!Yx0eQ*4}*t#1*K+rBgdE z&$8IpaT+CCt7^hY;$IoFF)%}X5XiMnBm4sD3d=mu`yEh{UQa-+PaNvdT~Wwhe{KFJ zE&|t8#LhJz@SFNGf|aG<>A%=4DfaMyeA6ROvJ+d;Do4*%8~(=8DYZfC!7qcp#eXf# zfQmct;a$K7>lqaC04RC1m>IeM0=b+`R@i+y!v@>M~pZZ za}s9*u+l{$T91iByzFf31W6MUg-SZUGvOY3?ovX_f;na-4t`kbcse$< z7qgN|t-L}bmEFc%vvBO)gfc~%<|~(-7)Ka@RRyT!g;!?zMo4m{t~^nh=h(EA(2ZwE zseeKDx%Fh^FpFG*MDn7lNTARGOI{Dy&A=rpf)eJ`SyGHlTi!*_`mu+;zpLsJM)6pc zOaXQ|s?N_6+8WL+_#57Kh#~kFkN{hkWr;Sv^TqsGNvL%&;U`D|e5H$dHM8nQqQ z?Ub>=)Tis};pv@D>SV{dca8vIoZTw_!(zGp-j-IsH2mj-VTI3XXANrq! zZ3GOQ?9Z$^2MX2k`zXy&+Av*oiMP)uklVMCW)kVS83M9DNAPjl>hfnJ*DQ=AGZhhc zwg2pCIN1bjj>nD@xx;=_kJ00kn^^VG`*|$N$~r~m(L@vNNOu@BCEY|ZJ~&AKQU$US zb;ej6CMJ8`R(7oa!Vpd5ZV~TGzf$(X%>F{&(omEU)L{ciR8od^3~3MCXXKN_q);?R zsQrbRnxM4j90QtL2KV#=5L4@bEMiZ_SY`9`p>qrY?xS|5mJn!e!Z;1e=(c@A4vZyR zI0%OE(*%Y|i5NNLJCmucjU`oG_Oxec0$z_XdWZ|Dh_lKm*l# z_JB;mxwv+t6)xfpy6PZ|C4VWZgiz<`<=ldmQaA`{J!algqCQfa`LHvjMH8NEOYWg( zctGV7*2J8<=c1Gn><~EL%RwkRD|*-6-8yB3GN;S2zJI{P3capp3CUT|ykS7mjX3EO z`gtBV1-+~GxCLHZ?CI?Z;)SG*@YKa+sEtI|#78V8cv_FYQMG672-~R3pGJyyb(7~> z;zGD}RW#&=X_5kao#?m})5?l!f5}6x+T6UrgQ*P&`90l9_KS$1mU%z1)8iA^*5)+a z!2nL7@bu6F`aCcMR4yf_Ds}f_(QVumwT0?;v0rOQvgdhE$itp7Oci@5=n{X!xsgD9 zw^1#~@6Ga*J69dl;|XRpS+JnCE3>N}?Bb=~EuNFda~m5S#sP;RTLFz}O`}$-58%Sx zz5*9Vm27ge!hH)cRZA4$Nh^hD_BS4;AdSbG1So8O5$KGDZV7tE;)>UlbZL}%+)_umvK##!M zLG@0Z&)z*oc17t*uS79e57ET0&7C{VR7?eKHQGIb1I}KSXaJt?gH;D}YS(yNW|C-Ex2&BxITrH? z=&G&Ht>x?Nr)aMd*e?k+h=ejTb)@F0MUO}OJo9P zX|gh;M)MVzQWzAHj2q#X-U{ZUnC?nTCU!m&8cC|LMG%=O` z9=yhyvTzoo=3R+(Ygd)gosO1Mbu$5>$Aa4M^xQnjy@6b5z1bZBW5;BqRTudLWjwIs zSKn;$>RDvj}3~#L5D8WpFI{a+J|NHSWaW%t_j3Hub*6tr)zD5b`^pfeME6 zm+)7jY7pENpJepEdY~-If8gBO+2{f zJ=Q|Ouw`F8Pu4en zD`P49T@K_+0F?p->o0TPav3&d922+bbT`b#;rbL8)?>i79Rwdy*RJG0a|yfhPBA+b zF9y>xd(&+NH;Rarz~Oz!=kDlTs}=_#7~LFp;scd0Qt~03N~^nF`ju1T`)9RVq>J!? zshP8Y`YhRbEi_E~!>UVKCB0XQ+oH!J*V;((M!W>ePRz|6JRVNj_p!Quck-pd4aBQ+ zxo=2qR_siCR>Qu9xtl>h7$oLVUAg~)IBxBnMk{p|waM-A<_?c?Bhd88=;n|>y#?3w zcc!Cl4N^D_*phm_XgE%EHs}Lrkn` z@hYl!N^-7C4~#EU(58cx%jD2?A%%ulO6)w0pd`|^Q(q!B5bOu$yHOo;6*#BUvE50z z^Xp+7)Aj8&k^9d1Qlkp+A0@KuPBmG1ceOwDdSdr#5YgCpmB#?wv_shN`)9wgHDm2G zR;M=%(m85UOGs& z-Mb-B<)W2A(`Xuqkw5wTT=H`;uU{Lf&T`7sCT^<^Imsup!AWw2=UHB27vb*_16kS=xjJN5OZojLQ^_uMuX3=LZOFm>|sq!XCh(PA$(`OWoD)UWmH~Av4L0Cz?4rx>b~gIo&{-LXCc7PjUaE&N@H5$gvR~ zSxif{c}!5#qBQj7V1?+|dqYP3Ry-Xzuc>V)^Ha{ib8(37 z{BzHMcVU8knKbl2A+13X67+nKraMrvQA+=HK9Nx7bCKnVG+14nAS92*0`)*EWMOo^wH zz-T`793io+l`&{-vq|(|2jr9)GhY>zN!a<+L_z95?2#fPu=!c~^svu?baC{?*93S^ zX2osPY}bP(g~Fqb=x*mKEd~rVgtwl=?qL#CH!2q`o>* zR`xD$GqJq|_vI-@!V41KJT#POzAlgHO%puL z(~pY|_NV-AZlO+EJIi0;eZKIAk-k~!%OC{Q*M&emazTakg2I@=D2eN3#BPU3jqw9` zeuVoaZ-ftiYO1w6?mZ-%HeE|h)P!eMD%t7vbxkmzoBY>e)=V~Y)3@JL4Ua={&Ur>> ztb}HNvqVGUuzyK^?y+n$VJf!9LvjQuA*9=PO{?=tDOXQXeAMXWO%)C6*qBf^= z0@d65jjXxwHplhtC}T8(ic6>8_-^Ly5-miKnS#B0XiEpmuNf*kygxa=cfSiwlF9Ik z8wUt#3qudro-``qVQ4UunMSe+1~9&anToXPqP)UtQU18+2dIMK{+wn5^@8p*E(oCuUMBm`;}5z&H^17ni-~q&A>D6}A9KVeayy^LOt*`y51l z%RftxO;(%b9Uc}5bcuCp14Brcc+QWGOdq79?HvA0hgwjs3BBu~6Mmhkd^?%dRF~pp znY67OGd;{#I%m0NZK7l|VGnr1I9MHh8p$9h%rY}E=uXyTyj%BJEaX<9v@ZIIJ)yOd zIoWc3lR6n78E;9QR~a36SP8zfsV7WoVXk57i@fJBbaiX)!()aY!)Q6#XhMsQ zj{)f8yHh_=D)Ctz0b$q358*PUVLsPZbCHgNl=z4djXn75@=}@k<-Paob~VgCbkXw; z!x%4Zq3W1&N-xKRX%WCDm~t_`{%!*(BfD5V{a~j2<|@B$IVnpgx%>uaHyAbK*ixUm zOqMl&ZiRaSLDo|fl|hIPvp@3av)Z)=j6Mm!Nq^>&E1he?M0YLeCUF|~;}DA4lz+>z z=o*7@Pk#@5S}%4-g1-sN@1AE1>^hNu_{Pr_!bMx%S;rpy5A1D!L! zv4GNP9~skezDa?j@`*$$wSNiZ$*;qJ?yPbP{vK!~qrd(jAw-kAV2Hp=<+2aWr>FrF7xUQ9tM8d5}T?}N2PkUk$x3enb2>c5>36qeo9yc2LF zh=OKc>ugIGM?RfXsQcwu(j9KbLKFXx!NBj^1E$6T8>5)JPRQ|NLKM@%`!T+^BYEog z-}cOclUnSU)i6cxXOrStdMKf3zlKd5e>fNchV7J7Tu53}fP$qwB~fcnWR=&I^x20G zu#e)Rb6*t#>~Apt!D9d8|6_cN6)HzZNUT)KHeIlGd~cFJYuOrQK>x=@__(>UXB_YW zSAOrf`6x{4J94Xs^l`ieB~6={f`=t%S=-qiUa@y=U>CMp3+%Tlki3)=L}05~%AI(S z+mq~Z5a3lPsOG_|vZ>tW%}Xpli-{B5+Jy{(XK52H8ypuwh{JRR9*-KLfF#ij8eH{v zsk<)FdgRm{2F+=q#2k{HZLiZ(s|I+DU3z`_6PE;LAZf7ci?A#4N;pF2wGTs+ijj~CKu~Y z0!sCfYZtclx9K|VyFM;Qk19x>kN~Jt@|oK62Z$?j93Wj$nnpxe>y!2b(Rg|SC7z#2 zAo&KfL`lc{g2!rz_PZh0xQVbSqn)nje2hAba9}hG9K50Syw86~Ly`7U-W6I^K&ynr9aRcYidq7oKTc=C|qU{q06 zGc9!keWe))YHup)PXH#azXOdLA*v+~I(mHa5}qo@o}j(>T#U6#i|(W!NS2Uq&-nvz zDg92vW@jX7ag~*NM;q|*?Jr>x?4Q3Y=qfgI>GE3U%l7zD@a%0?ptZ05?FSnlVrp@I z2Ri}%MXArpj~2fGfu>KIVLIt=pi3i}^1T?^g1@a6P@5}=h+DB+(4rRV*~xj>ciI}5 z`orPHCkT4Z5@EG;(~)qeo}Vf;(Aop!J}zc|pYLs;Jh>^)jE;F(m9414{BI`h zNyZz|F@xU(AAm%a+kJS;hR%OmVrUyLa8bAS$}Skz=iNJ`p`tx=T(Nlz{@m&N6%+Lh zOgczVd{ZgKOm8&L#dI{0MjpV0mXn}y3%l?;@Etpq1EJ}~ zu1&-+VTADX5A~m-!C z6Y>-C$lDHM(BzE%o?F^7D)lJe0gf6uq(pUWxBJg24AEeVR(F5E`E5^^u5LdaV3tmr z%~bl^#l19r_ZICVGh>9uo;hphiMFwpzf`JDi0>%`tb_$T0a3&F9Rf>aOH@E@iP1)k zC!kReK_WaI+0T=8(%C%I&pjRFb5B$^lP8BWCRr{w8^1Ie~=$BF+Rx~6LI5oUnvE{clli@h_E z1>rCBU;s&`Z1njLZw^)`1|pSITo9iW5mA6~y|Kg2$#CkkynyJewmKFu$x~gUXpHy} zp@%2;0MU)qC^?`gVE@URuRWMrBi4`a{Cw)qvgnylznV58KM)L>$0<#bw#VcQz9}l` zV_(7O7zd)x13u)KbS;`*5MD5hho=HE8D@Bh4xykw=A$Mj8o{*SpiJgynrzI4QT6RF zJu#i^*~ys82q$PrE3z`~h#~iF&YCu?tp~7?mdB<~YIMv3mR5ykV!H^SQP8OGUyt^y z#B6nQ{!!0xSFpovI$xcxeTvSvlDFG5c9-~qa2bX8OV{72vc2hf|IysbAqmH$j`QDK zYh3YS;OQhsbX!Z4{oMyG%_pCqjHIz{OnlRR@ZRpWRT;nPNUqcMT#OswOMh#!Wj@pp z8sE2s`wE>!^eiY(-nsYZLO-xfKa)B8+4xxFD$;jnK7W}$k6Z5O%C6xuy5D~A`L$oJ z875QB0nd*;n>(OiHIlo}?XGfdoOeFT@9&1R`|0$c^`B)0RAjm?~l!4+7bT5 z(#ITYJG`>Wy4w=%8e+)?iDCoSFmO-R6JIQZnA!asYEs#0SX@7H&? zfRxU_=q)@{A5(uLh4v;1z-;bFe*Y3)y}xFq!&Vf8;u_^k{P5mNH+G6YPfzgw@$@b3 zO#c7>b3Qbb2u*c>-3M#^bLC5Aa> zBVm*CVZ48LpYL`3{( zLDpfP?wie=2r1KjcsJ+n_wwgqZGOr9+G0NJNac zADmyzqe%G6)8fYG128*i!oWgP{}&bF@V8hXFY7AmeaOzECIlp`r~j+od~iq)=q;mm z8Y5A{7B}??j|Lp&2j>q0Vp$=&!d9ZCxjc!^c)NM{w2%lbyI=K89i?#J6!`K=u6BN= zZ(SC)QxAmnZMX_YPV1QL_*f&4;v7A4tFpJMaBhSEj?4 z24%8W2-$lR5Y6xUD?9@&Cy~O;E97gK{DZZR1sP+nN`^qD&7M3BZ#IQ0Yx=I z{)AwX!w?(4e{AX{I4``rpj|V^ys9#l+umlUZdO1R^bIH8ez%cBA(#zLj%mn5s!3#F zKh>2IzI5~&%V+h909YWn=B(81U9{T{d2_vWq%ft%IH1QQUy*b@8*`@1k_WfZ_f?Zp zzKibQ{O~??ZJYttTO%unJ#@5SfT&yXJ0RRo09yj`Cny{PD(fcNJLDGJ+L+hWS+)t> z^dXQBK%iXOFW9c2_*Tcp%3n_Hvg0Tg1O>+jd_CRQ8}xP=w^07DTU$B%q-i^djz~6+bs+B+~#1iCntX0lfk~Y=u12u$G1U?&QXdGijX~C;bOHMplPTG@qjBp|4CbMF zy_R&8C^y!6Me41^>N<~#iVPj-eSk=iPug?8+WrIO0nsffV|6OGRpeQs_ABFjKEK1r zO0roP`|$|q3?NelcGl@v4S>^QmOt9KdGk`-1WhA=sw1P>RBDeac~)kCd}DXCskoq@ zOS-g()42D(W&I$RrPC#*P_qQOV2Krw`_K`V*5M}H+|hseI(IOB>(5|EELM}DxFiysozKn`i-+6+)$HVK0O$*}Wc+qsURndWb<1lbloBu>bHs!t zZ2j|XyDt}{06lzCGTA~!F~?$%PqBy%$+1{K(!kP5shv8Kj-o;q>T;rRC3m;N<=bi|NDqBp#;a=WM+9h}?th>pW@uDwXKj*k+AV&GyuxoYZtXT(Kek6X-J_V4 z)S!JWUGmLYrYC9jCTUkB2yBJ$X=m|rt;rgMC04zvds#~94IF;RspP-23wnF=8ImmII2BzI~ zvO+sJMeIwXW1i2rzmDJ6le*?O)K-2zI&XM~(sVL3w|?zqLRO+|TKijR&?C?3c|P-A zbqj+?GHQdg0B^IK5ALm0w4Sbf>!#7I>@Eu`1w|Rj^TSFLZRXYP=-YqW#;=py7J@zI zn$d?uu%4wpqmL2PK}d4EMFUo1n#Jogn;i1h4w;)*jUrBl1~0?&h{Wc@MFo-Kvf+<3 zgw!7BVTGIsdDFEr0X@uz-1DuaTTj2W<&$P!lj3b>=D3~1nR11Bo+UHm9>`=38}K1+ zM}R5r==_%I9PK5B|_2jK?>eV+KSqvPN{4s*ySxw-eos#MUT>{4HW(^_+qvqSi#sB zejh;Cc=>#VR8-{8J=7PU4GjLZxeu#EXS{U161i?2LYdl0dE?djL!=`qwp)>~CByIR zcq)5pZ706$=8T0~zh!BlQzT%IHiY0eHfNAcR|RFfJ~{lo&~#iqfDHz;yv^hBnw&<1 zPoPGDnU-I>@tCR4ds(AtRUT$L*vu|U5~b`vQ3Bo8Bn&v12;iVpwRhIGhaW!SGw2;YpD4jC5opx&D(K{;F9dy;t@!9Ee9Hbh z>E6%RmhC;FUp-mU?XI$i%j5l}$KByGtDPpq>v!oB5kqF%+R)$hbYV4z06Kg0Le+nu z6J3Q(^1fl-k(pK?`xwQ{Fd@#lCJwT8zAq zNc?yt6_?C7TI_BlpjNtEDrv%gxyK^I^YaGc6Q8)}#mykgb54uHJ9vVgvM*t{;FozZ zHm4Fjqfi^6p=-a$`;6PONaN1bfW(JmFEX^OHl}B_ESTmX)?ZkU@VP>@X(8Yp$z}2T zeoHK#-(@G~GCeZW8U>s((V;Ovu!U0cUX?jLG3TgCj45$yeifjCR{rX?{?0#fd^uIC z>xtiOqXStPA&{gz-*`9fXn!9!u0^volmr|~*OuUUUrSF>bI`*G$fn_Wn8KeQv-fAY zBI}5{=IVykOOvGKx%CAmv;5f)*Hj`T4@|kAIby6F)^s>YKF~*m=I5{K5!RTUwmv#F zAKwzY5jm0CR~dDyQ>5f~mwA9I*c1DTQPeCbCX40KTJii^G<%AsomWTq@VYtKveBAJ z$Bg>EW4)sj(_UYoJ7+x8k>{bFzTP9tGA>*D3nL+w2IE=ZVoATh5AAx5?Zsn#h__Yv zw!}@Z_J}D;8}Zg^=Ita4N}F6BD^-p&SUV_zHA2-zg$wQYwfuc3Q7Lk`Hy{I=Xg7md za9?S~`LDPGF!S(y`TgoyO6a!MuE$Fb#{9DHV~NUSUfH_0jAWI$Lrr-~lCFUxNN#vg zBZ(PDA^iT>L~MXhp>tb8=MENQ@a{FA>L^i1r3FI2q$ZWTA5r7CW#6J6wiB6hdi$nn zQFQ`sN2S;o+5CJeeLfI1Dbni+bDX-D2MFc_qQ;|?z8`8Lu{Gn)1-F%)Wma#gtV$vx zm${y(@LZE56h?1(|ALsN8!L#?#qz#2b2QEC)vEh2Z5v*e3zb1$kh9oohv=o7w^~QUGhKkfS>mL z`xJcTnjOPN2KL(AqGO9P$3&cydjtWQ5-q z34KJp>8SmSiU;Pc`+m~snKriBT)~N;j`I6 zab@M*?mw13McCMn{R}ATOpcYco;fyF<>K#rAWrT&k=q0=o8#_8n4N15JK;g+#Pxn$Dv=)7z*dG8gm)maU zIkB$rIT}`_rVB<2C>>6>wgYop@a3Yyll>Zi&wO!RKr6`DMqBxWb{v~TbqO^Ya~ozN zWD0qZt7#>gWwO}`3An7Rc9&jZq0;heD)n_S(3cADyekWDBeU7$_oEo%&6xqo)RdAWY8F;Cm0BzEFWg$^8TW1(Jt_a3wwL z)R_-5JJXo=6#noB`R;POCS}k8su^Ovn{w$P)j!jm2eSGlQ6^uToYrH<=PjL;Vq+R) zSiZa+H39T%c5GGqF~TCJ72N|}mX4e$xdX|p^#g*y?7y1dY41GTPI3WDJb(Fer*$Y` zsuu<;jHG!~3|?h){lFTHZh8bV917IWrrUsQ~I!{~97eK6ddzU{j*z*jYu$gG?5l7E^zINjL{GvB9oAYzo z!O;GRhJ&izJaAS|$`R5Mf@{)#xl=69Bn-lpJ1YROkp`}466fGgRv@qs)3Q|K-q9`= zu&*ZPsW;k>BR=NZOh5q}1;CrPS$3c>Ki_*|Umx`i<+>*CCXKWbsgkLg&F>#*0aSQd zI6Vfqn19o6?pHP?u;SM~`jAP=p<^>h5FaV6K#}X|MU0*HXXlgTtVfE%fR^=txXPyq zlbd7Y^?G2SX`}7?4kM+u6L?uyFyk3H)ALVsJmx;&@4L4D->CfD7OtrdwVV78v|q_- zn@PY>6B@APo(AlkKQ%-i@#)lbOW5KUp7};RAT7FIRx<`pY?U8qE8$ZYV3^9CG>}&Fd0ozv?(j#1!K_@Gk6Is=IpC&b?1?7Wsq0TVgOQ z)$m-v)#y{4ATRbfP`CjMV)&HzI=r}WpSA`iynbcr>?-^Un+h^XQk608|Auuun$|AT z#6olfRTHpQ`KPeOek>k6OoM7S!cBnK%e^Em*`iWzBX9?193jyVoL1n;faxE9otquK zytQ-pAaak&?WrEW2JZsDh(|^)xAP!Gup^g#3HA&2{e|xTy0SOt^|pP1?E)U%4};Uw z-rLBZ=ktZUFo~L%9!-|+UDqTg-fV$Yo&~0A4&nYsEKB3cBLu(ompdS-3pE#+NPo%agLQI`XxE9fvGeBQga5Q+mE@?N+jB zyZefhW7w!-cSsF1^veUlZc|I(-w?15vd?%7;VvWd%+xs}CsAhP2%!|iH+tPR)ImwjF7uh5 zqDvX_~6ApVB<%(MFcpF$5Zp>b?D8e_3_wUWG{`Ucf`7b_N0>bK7bS zL6E;AQ-5554F0X1$7pMz!0f=yA4|D2@ZgUyx@XVc;x|VoJ)~6C=!3$iow!4I9uw(t48Gd|LB3#Oz+Ez#QZf z0w2FQgApC~L!70fNC3{%sN{!D8FAsw6{|VSzoa%j6P<+f(fZ5}d841YKKz@0fmYo* zVTwk50)je)8d3vqOT;d{cna_Bv9vQ3KqjoMk(ol-^1fj`QCN=+d)U6-N60H(wU%Z+@{9#O1GK|N%H2hGO5ehA z>-*jHU)DYG4nKW$v6Www$9~^e0k~2PdA#YRI%ff&TRZ#BUkzmMxT7SGbVe7C+E;tWFQp65d++v2kSOEyxvQzix3iHI>XiTr1$U-0TH`TiTIhXR>Rz3tb=O-(}* zFP?>ycF-MqqZ9|qOWR4W6Yoh4IQ-`kH9W+a2}N>?P4LBIPK&x=x*>NWzYlO|6BV8`B+9&AT`pHtx-N~{So6|YcsVlXq_Ct*w+6Z;(KQzTW%X563q{j(S+4&R zF$60{O-7Kz+oi818XU!%k5TSn#$&q@8M$Q@w~JXoU{5?dE?J{|gvICYcxh#tO!N>* zKLi8P9+rFi;~k4_&Q`&Iu4cmZKTt(3_4GeSYLSTcb82GhSV4KmJNaaPb_5L;A0J2S zW4ykK!TE?^cGNkShHvg|@8CkTMlrtn($pIX3l$aP4HL2We+sxvY`i{8pJ>k;UDZ3y zqkxQ`1{?a!3xGwS1ppFoQ|yN1vxgvEV}*Kbl6bd~(Yw?qyZ`;OGy^upvpk10Lwu`rjBAZN!Y!aA zxnu6vWzPLn8F*VzT3E9I>%bMgq6qBD_Z-#)HA#@9!_B@onjEF>K%ZF)U2rpMmvsbw z=RoW;B0CtbLw_neYvg#}6J+DqjpzrB>7B{_-(cLMQC{KJ$PT>}96Go$bQEq;FK{;( zp6b58Qa4=779#*6s^>)do6G|6Ht+4X(x=bC~U)zNDF& zEa)($j+=&c34gWAWfT<`qpS1dCvT3&95%>!7YKK$9l9Rr4qgw@x;$WXliO)-fmFf~ zJTQC6K3$f}!!K$D&d9-(hfPEpUI|R+7f`{x2>W)U?Mo=B_&*S1qqXtk3EzXkD}!fG zSa1HEoc!L`w;5AeNUw}BF@nvIvgU#d3kwv9M3V5U-t((fcW^yDJjV*!-0i*mgwDp{ zUyij^+OY|f0Zy+JqVc`g?O|k-H!%amg_~2FPR$2sxs~e#I_2ti!7Sz2LBAPytX1IN zC64#@+n;I2XtVQa2U4D16S2BjvxjLh?_v%2i{7jMj4^DzYB)`3-~RdI{f$4SCSnbE z($DI^k82li>5mmxyi)mj{^IkQx?T4JwV&e=;Cz*t?U}F}=_*I9)6|SwuDy*Fl@bA8 zto&K7*7WTB2{oIzKwW}mk<1N0Kf9GX%+blUEm?-raAkaSJ+yeG+*l793E>z3|FFD>^twqyE__=o>MH=rvvVx!vj`vqnJQ-YII zR?+97f8;tRj}L7GZKy|8oi$pQhRy%Dcl~V;?TCkiU*#fdjQI9vk#GOp7=(OdHsG~;cs^SFRd_XPtuu%F&2kCZd9iWPEIOc`jMd;E!+ce%>;d1?}?p3n6a z-3~pTPlp&0o}bektdxM8b!<8*ZTgL@fAspj&NHVu&@MEsFYBnj@&V% zw2q7j&QMXFapDtZ2czg?EKizn?53cpq>TeVLm#;!D^Y)?wbgWKPV;o;QLcWZsF5i6 z(6TC*($Pel4}{)gQS0&S0ps8N&cbdK>sBBH=Ow4P@Eg54dvDi+%36!D*4J$EsaF+1 z%;tvXqK;s~$f;@L3Elc1mnJ9RZI(jdq-=%NQJbErhm!)I&%Kp9-D7;H_d}8u`VmmE zI@EOM(CM^&KY&nmC%5oz8khV@-EYZZoJ;3tLDSj-N=zq|8{mVzYZn*#GPUBB_-+eJFdm(M z^Px>bw@2k}S|yDaw}OZ&1NJwBZ7vaymb#3Ct8&2O!79Q2F(f8&O&lX87G6Hz$w8e*U3f;aklY60~u}rE&$J&f@}_u)fX4byQGn zRRi?V=+KkIkC`td?aoLlvJGX7>8m*Y1u{a0&;9?XxJLcG6Bjlq4$6Uq10M8+o^UUZmB%R#j~d(ez-_RB-UViTpAG>!jbnz}+N^D}J&QPu42NAKZKle%gv)z2E=V!IyOUS7fHp%riff$R{AU z1Q3@moeL^g){LHN+eNv$x^Cs8DlMxXA!KV^2YBWd$=Jx`KH1b2cmJb_KaQO6AjN1G z?=D23&6vuQK)8&;%mV3anCd>9UM9IoDFCE@eBgCq1n|7KLtAEV`n(dCu`oe= zTn@qI^Kz!{`<-VkZuJ137Br68_|@-e&7oy9)DG0nl(fSUYQ#1p3B3N^^{2BvA+d_|DgY7;2Ifz(R* z6s)BVcsuU`Q|4zi08RaJfDU`!GThr5(u!IrpO^sB2YqE!)T*SH`Xjk}WO=&oyYh<4 zc+6=uJ2v%wb<@XgJE49pR|Jpl?_Q8{LzZSDt@o*Eb%#+riRI}?gLx%YrCMLTCTnY?gn_FlSN*e%(+ zg1ln+0+2|~RK$p0(FpJA9Vt9r+hJRKZ4EN-ePt$?_(8m=jUlUhRgm>*txCddX&52)#Ryq3Ybd=nX*K_Vm=n0O9kU z7(pNr-Ipr>exnf1%o1}|h{T;PeQVp>MZXbBkNdvN*+rp)2&yi(EdkxxYALW#@ntk$ zBcCot*fYB%&ip2yjfp2i*=SA3KS3X0SvHxMr83*GVWh~Z!n+8)cJAmivLcOAw~LDM zHrIAc;>PHu2K3>9wi0bBn^Yhl8|oQb#l=?$nD5DAU*W*ZT|Xn|;}NF~tM}0-fwVQv z-QJBl0C9s37ipFb=ej)Ow(;>$>ke}6BR^0}%1VW~`bpLm`LowTVw*Vc<#-fk;8 z`t-(EK1VBfz=bVU*-HMtv?@VQ{Lq+=GobP^h1LO|B^ggAud>TGkDoG;;m)JKH!$@n zJzRbR*=*HxWGw+>!>bD%guf9xPS0($HQ8JDP=j8a)MB)lm=;UlF3~uBq zuOAh_!D=|)T3Fl;S!!7f-cm`Q$v;x#YpJHIxm6=$KQwO=ziEQkE8{P8B^48> zjr(pT<9Esf-c8v*hXP37;`hBxGP7U;Wy;U&RzWD2q*Z~;RLu;L-9{kHRfX>5Xy`7h zWTK*gYwWlCEh}CTRVql2OT9}t6>;S*m83ZQXgg9(>wIOwUHmXvpS{Wng~8&T40v;P zJnpjO^b(iCFxKPerr-37A}oBvhza2r$Nua+ZwW<>ZL$s+7oK+M%m!%^FO*7gg{cNe zcXjoA45%wj`>gw1i^lwG@o)5IskrT_M+A9-Vx|el`JME|*w(9MI*K1I%O-t=b$%VJ<#>biAH*4PMRX5x+hy|=%1C?T#`AF=@`7m4pS^8b}9BQ2-MP1#) zyAESruhKcg0HmETIx0lwHDLMo{&Rl*@lei@RPN5ycZcasU3Sj9K$Y5PKHsk$tQmiG zd2VZM&3-E-Q8KBhOd!%;@|xR2i}n$gM^>+VV3@3`_WzcgSwps$p{Rjgm#%(R$JL4INiSu2DvIgH7#DfkPL=x zR*}|+DqfT6FP(zw$hpb`eWT+-BCD(}Ybg_}M6R)Nq&52|-F50Lonr7Lk_4#Lr*{EA z!0KprYa{6*omR~nv3a~Ar~s1F1*dl_)V3?=V(8DXHuNT4t^lpuSm0E9Luqk|Zmr8P zio6auu*>v&lBUR7%q^w;7;PK+H4WM;E6Ns>VX#-pF!}5`)yDWKIA;&cz(;hOzf7`rO1I$e+E~y?kolY=*YD?XFB!h7(<*TZ`XT z0!X0Y+9|Ao0v3^#xW$fv56B6d&T5`tPqHZ zh2$LT{3G4dBi-qb@sLw>(Qkx>RJ8#PRXh%>vTw@NdfeaGJI}a3Jg+M4E&6k5o~oIZ z*H_OSoV(u04xkUv+DZzu6Pzwz9}u>K!J2$5iMn6?;D&yOaB%Lw4HiZIy;`0FH`?7-DlsdgNl_9noBLZYfKb}N*JIVg8NusxXeXZhAZV+1 zq8A~p8yf=phr|o^5ieEk7o7M4D)hGbwcbkG7^0Ir`uwjYetM8fw|7rUd*Ob9qBUx$ z+727hZsKb9``>R)$JW~Ap7m!FW)=qn<2J^$-A%)DmHe?>?JhjV^Cor%HlA%$GhIM; z0thg)-y=M~OqeK9@GW>6Eh0wzSwW36&d@|JwzW*b85i5<0Xi&|RQ&0UQ2DX^ON0qR zF=-*b6~?}Cj7?HRJ{+&IoIcu@wU`R5lt7l5QKb^b*f{;@snwQ+srX-+?G)V)JqUxh zmhTN$5{=s#*=xKq3X$1O2V`^!M^3ZbNU8M)etnhnj^^7})ZW{9p z+%#{boUhL-0VM}cn8ZWat?a2~FV$uu_XB*i*8MK#iExae&q}$q`MAup=fccX_;SU+ z6aeU@#^=4qQ6h59sRln$A`fKITqg!N^?Wt#ameMwECq1T{Vs&5Zi(|Kh$dA>8aS-C~ut=aZ+32!*s>=6u#ROlmq z6+UJp4_C?*{U9n-m1{tH9vT-)W4U_iM{;ZduN_U(UG}c`7_f}WwgB(&+)Z@!;rGSl zL8PQ_dwqKRt6M%j()G)tk33fTXKnuKZV5`r})_C{P|I|9z10tk`k{q)- zwquW2uIB+Q`+T$ex2TZ>J2t%V&1-Q~fBnon_G&fF1Yzw4sU=atO`_5@eh+VVaMd|W znmH5$I!rTOrkLv#YCjTKP^|1E-AYSPZLhL@+9@F`#p^$N9?!DwVgwX6=A&ZvccbIw zva_A)zTE|OaGIf0ON))0=?}CltH?w`pViTXjAw-Y(puMLT!>;e&gpNvzUu#kZ%_bC zz9EeRz2c_aXDJ3<|MB*E;}-X2l{rE3Jdwze3T|W9)8~TX-slIs0_mltzQ{UKXYkr! zHuYYB*Jp=6xAGF%my%I1&urd{`3b^!i zvB8T;p)rG60Ttu1zi1#dTbY{sY8S}GfVCAzAL$G)(dkVrxZRiCRF{ZVzwOW#-eSN+ zGH|dRpg;j0*)gJJa~o5^U6El*!^<=aHtuWe^nA#a^GoFn-MH@_r~# zA*x*&HQvuGoCKZ;uL|DEbM@~_XNb?I&YPj6O9lH&e|M}-tIHl90p)uhtv55B82a?l)lfLG%lQWB zo`8`mN0st_9&9bd2vyRlyBu_Nau(KhgUWAi2n+c`N_wvYc03HkZ^O#(b^&iwy?S|Z zKVI;}$mMn%Ct;L?J7sCS*O7ql^q*UOdT!0MSG$`~JPECOiDbGtR8uMwsaa`cMEj}8 z8Z{FsqX5fM3ydu$a^s8J->yEM}&80p* z=0JF$dSZWgC)EaFHyx_@_6nB&t9#;^uWM;#e9pHL<<6j5iwiYdhn zr<~!IW`K0;B`|uJBoU-b_Ox4exylQ3#n2}(={DwpT}@^s5;>2vpBWN9I{o(7v}1>H}<`T zk(HcC<&#s8QOxc!7OVgiyOiOVL7!O5u&QeO7@7QQ&$Q*vt9?ej_Ln}4mC*++CXX>F z5TPU+R-BemK%8uiT&~#Iziaerk}Rw=Y3gl{C@~nCe*=Wt8i&j(LiqK#Use^(#_8wFq)^}& z-rw!42@#Z8R2lFRPOUp!d+nKMJzd>r3i7c!15>cma%QPHOE8LsIRGI42pGxs<3kGU zo=@o0q<_q98tx?MODgvHt(u0~n8lw1^8Z)0-S%&z9K8 zJdf-$^{IluJ!>F2s@+tqxt3@r&M)$yoR5$Aysgm0OA`&CxHIgg%5c)0dOPTr`NW5M z*=*d3L2@@&4N^z-%){d9$~am)lzH%PN+%>+SeQT1Y8AJnrX%EH67Ow1Z_HciD74yo zl06hp$)9?w)?`vR=i^?CIC@9eT}!&?#f^f;#yd!f*}uMDxB6fg45r%pC-CfGV4a(N z=V#T-D2c0B7sJp?%0&75lzymJ?H|V+@^?IK(?=tg5P;j92ZrKaa%@nl3hA)Y;(?z( zrbuNRwi#*eH}2>F+I=$TgJB~Uh9VD!UUl7{DylM`)lTL~mQ&@k5=pl`qc)wL3CaC6 zI@87E>W(>Eu`xx3h2q~$t|cZ4L0tNe7!ULdeEBCKk^l1ElwMX+776ihENf|Bn>2Uw z2DA_X<`)__G#K`4Nc>o4d?9{8vmpQ@aa>bb<`j0hwYrtvc3|gC^2FbMjG^DC#f(m@ z5>$jc;qBH2vv57G{&0)3plVoPMd;%0s=OX7!0evS%BS7KcBexOj36Y*kX4ZU5qsU% zvQn%YFq>w2xvv-MRlVdHHl2RhuRUIBk%bfFa6=`JgL{2NVU3ZVz&4P zT{3|@R-{4v)fPN-A!32pwmumw@@jfI;hcUkUxpMbFjHPb0sl={wr+OI{I=3GRdusmb5eUB}=z*ACOL`=${rQrHy&-6nw~~({JM`Bb%4SPU z(0`!Y=(HA1S~5H~*f8zjZiQ#-1p>(~_QBG4dO$^)#yT z_wd{5yCfoIWK8RM{>|6*5dp7ndU>j3l>AhA&|5FO(ms1eru&fF=rnQ{@iw)~nbe&8 zLb$HtQgPLePsQWPOCq9y=da#%R&)xRe42YMrB(S{eJ9Dw5?3aSb^h^c)}f$F@6GpE zQeItSHaV3dMVZc-TDj<@4v5jeAN&&wABT&l7UJ6YwzoF4fk{H(^Q!^O#dOONIfVvJhn12uG0N; z_55vA(XDWeJ@I$U0v9MO6H%d|>wdoS_|%2-gHlhisk%`Q7WxcQRjtGx+$&o;{i$j2 z(R#>x1B(b>D{;VHnK#iA$9Ju!s9jxGq@$0(zNtQ@X1M(NY5DNtw;LO6kA9p*mE+f2 zTtkOO4Ts0S(97{pTs=jbjLk21gRo~B-EQ4V{10S+Knkaes4Rw(IVE#XhffyU#h|BM zmw{|i-ei4_ao5*ZadWOa(f+3=uzL2QF~w&bh;4*j=mlen`P{tyn7Y@-UfbgDebZy* z?sk}MN5&b2ahH<)ul9Q8dzEW?Z;b>%j)A1By9A&k93DH=6!ox%t9PQg7RU7g`wR{g zuuiyONZJdVnCdC#_4UCOTFixe2kU;57M%zWk%n2{q(N!MVV&`Hu*tDf@~NEivdC2m zytM_rU}AIZwrF#JrMY}k|L0U4dNS3YQ>xP?^w$OAH^a_)nV3f^*@=#u9iF|jX5k`Z zyt8VtP{5rioH<@x@usK8e+Za#S0-Cbi?oD2@w<`H`-{~?K=4TRhddKs##*%dwL1!F zeLN|k1kN>iWKVXs?bTjfLzj%7HX-AQH}7%GAh9iCZkggJ0mqb5$xhh~W>|8sa(rlE zDu!P&ZOsFkyz4=se)|t}^+`5X%1Eg3 zStQmxN`}kY=K9)MoQG%cLU2gK`lumcs(Scam{X6mjvob~W`L1a6?REzSJ3;?z-|8% z0&+Y;exNy|zfR{6S1A#}8N%kg89669l6Ke4qgOu8)STn#ivLT{B=lVS{?{c4z4+&p zOV$V0?~m$L5Aw#Wjx-SDv}O8PqB?q(NsM4;7AFrl{q+9-K)`A1V^uy>I*jDfIJ5dl zTH3f56pPam>Rm{=YPuXZRR!Jn<~`d|;lHt1VZH99yby!$SMuN&>fQq7pcp zIyew@@-}){Z(YX8Tn%)d^Xi`8ATPrYVRTbS_Om;@$C~d(x?Tsl)-v8}Lg(ZB+iq(f z0!MD~pneGO^2*=erk^Kc~vV1ST6 zPPgtt`TH6qr?Oqfja)1xkoQe!-|qK>nvcb^Dgf^)NRH2(Roy@mOATzUbWhpQFgX7XO|_-&K|8LAc#%BTQb4`@z- z94-VlcG#8XI>kBL#@aKyaCX6Q4(G9+A%0DBb^V6EGh*&=5My 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 Wis0iHI-1AWU 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%0Avn3xwPiCFi1(yplP#6mDe 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 z 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%0AIIcTm)F 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

`7yJJhk&3dvIE|)(mJUMW8%Yxmh1;^ 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
CZiS% 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
Oq=J`x1PuNQ}4 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ⅆ-t1V-#S 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�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

}a-ep0aZS>#CQ(51gESRd}7kkta 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

)Ze(8uf2#M%^-Q^m1fAw>p5x9fW= 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 z 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 - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -image get_image_from_stream(CvCapture *cap); -#endif +#include "darknet.h" +#include void demo_art(char *cfgfile, char *weightfile, int cam_index) { #ifdef OPENCV - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); srand(2222222); - CvCapture * cap; - cap = cvCaptureFromCAM(cam_index); + void * cap = open_video_stream(0, cam_index, 0,0,0); char *window = "ArtJudgementBot9000!!!"; if(!cap) error("Couldn't connect to webcam.\n"); - cvNamedWindow(window, CV_WINDOW_NORMAL); - cvResizeWindow(window, 512, 512); int i; int idx[] = {37, 401, 434}; int n = sizeof(idx)/sizeof(idx[0]); while(1){ image in = get_image_from_stream(cap); - image in_s = resize_image(in, net.w, net.h); - show_image(in, window); + image in_s = resize_image(in, net->w, net->h); float *p = network_predict(net, in_s.data); @@ -58,10 +41,9 @@ void demo_art(char *cfgfile, char *weightfile, int cam_index) } printf("]\n"); + show_image(in, window, 1); free_image(in_s); free_image(in); - - cvWaitKey(1); } #endif } diff --git a/image.darknet/inst/include/darknet/examples/attention.c b/image.darknet/inst/include/darknet/examples/attention.c new file mode 100644 index 0000000..cd1e579 --- /dev/null +++ b/image.darknet/inst/include/darknet/examples/attention.c @@ -0,0 +1,459 @@ +#include "darknet.h" + +#include +#include + +void extend_data_truth(data *d, int n, float val) +{ + int i, j; + for(i = 0; i < d->y.rows; ++i){ + d->y.vals[i] = realloc(d->y.vals[i], (d->y.cols+n)*sizeof(float)); + for(j = 0; j < n; ++j){ + d->y.vals[i][d->y.cols + j] = val; + } + } + d->y.cols += n; +} + +matrix network_loss_data(network *net, data test) +{ + int i,b; + int k = 1; + matrix pred = make_matrix(test.X.rows, k); + float *X = calloc(net->batch*test.X.cols, sizeof(float)); + float *y = calloc(net->batch*test.y.cols, sizeof(float)); + for(i = 0; i < test.X.rows; i += net->batch){ + for(b = 0; b < net->batch; ++b){ + if(i+b == test.X.rows) break; + memcpy(X+b*test.X.cols, test.X.vals[i+b], test.X.cols*sizeof(float)); + memcpy(y+b*test.y.cols, test.y.vals[i+b], test.y.cols*sizeof(float)); + } + + network orig = *net; + net->input = X; + net->truth = y; + net->train = 0; + net->delta = 0; + forward_network(net); + *net = orig; + + float *delta = net->layers[net->n-1].output; + for(b = 0; b < net->batch; ++b){ + if(i+b == test.X.rows) break; + int t = max_index(y + b*test.y.cols, 1000); + float err = sum_array(delta + b*net->outputs, net->outputs); + pred.vals[i+b][0] = -err; + //pred.vals[i+b][0] = 1-delta[b*net->outputs + t]; + } + } + free(X); + free(y); + return pred; +} + +void train_attention(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear) +{ + int i, j; + + float avg_cls_loss = -1; + float avg_att_loss = -1; + char *base = basecfg(cfgfile); + printf("%s\n", base); + printf("%d\n", ngpus); + network **nets = calloc(ngpus, sizeof(network*)); + + srand(time(0)); + int seed = rand(); + for(i = 0; i < ngpus; ++i){ + srand(seed); +#ifdef GPU + cuda_set_device(gpus[i]); +#endif + nets[i] = load_network(cfgfile, weightfile, clear); + nets[i]->learning_rate *= ngpus; + } + srand(time(0)); + network *net = nets[0]; + + int imgs = net->batch * net->subdivisions * ngpus; + + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + list *options = read_data_cfg(datacfg); + + char *backup_directory = option_find_str(options, "backup", "/backup/"); + char *label_list = option_find_str(options, "labels", "data/labels.list"); + char *train_list = option_find_str(options, "train", "data/train.list"); + int classes = option_find_int(options, "classes", 2); + + char **labels = get_labels(label_list); + list *plist = get_paths(train_list); + char **paths = (char **)list_to_array(plist); + printf("%d\n", plist->size); + int N = plist->size; + double time; + + int divs=3; + int size=2; + + load_args args = {0}; + args.w = divs*net->w/size; + args.h = divs*net->h/size; + args.size = divs*net->w/size; + args.threads = 32; + args.hierarchy = net->hierarchy; + + args.min = net->min_ratio*args.w; + args.max = net->max_ratio*args.w; + args.angle = net->angle; + args.aspect = net->aspect; + args.exposure = net->exposure; + args.saturation = net->saturation; + args.hue = net->hue; + + args.paths = paths; + args.classes = classes; + args.n = imgs; + args.m = N; + args.labels = labels; + args.type = CLASSIFICATION_DATA; + + data train; + data buffer; + pthread_t load_thread; + args.d = &buffer; + load_thread = load_data(args); + + int epoch = (*net->seen)/N; + while(get_current_batch(net) < net->max_batches || net->max_batches == 0){ + time = what_time_is_it_now(); + + pthread_join(load_thread, 0); + train = buffer; + load_thread = load_data(args); + data resized = resize_data(train, net->w, net->h); + extend_data_truth(&resized, divs*divs, 0); + data *tiles = tile_data(train, divs, size); + + printf("Loaded: %lf seconds\n", what_time_is_it_now()-time); + time = what_time_is_it_now(); + + float aloss = 0; + float closs = 0; + int z; + for (i = 0; i < divs*divs/ngpus; ++i) { +#pragma omp parallel for + for(j = 0; j < ngpus; ++j){ + int index = i*ngpus + j; + extend_data_truth(tiles+index, divs*divs, SECRET_NUM); + matrix deltas = network_loss_data(nets[j], tiles[index]); + for(z = 0; z < resized.y.rows; ++z){ + resized.y.vals[z][train.y.cols + index] = deltas.vals[z][0]; + } + free_matrix(deltas); + } + } + int *inds = calloc(resized.y.rows, sizeof(int)); + for(z = 0; z < resized.y.rows; ++z){ + int index = max_index(resized.y.vals[z] + train.y.cols, divs*divs); + inds[z] = index; + for(i = 0; i < divs*divs; ++i){ + resized.y.vals[z][train.y.cols + i] = (i == index)? 1 : 0; + } + } + data best = select_data(tiles, inds); + free(inds); + #ifdef GPU + if (ngpus == 1) { + closs = train_network(net, best); + } else { + closs = train_networks(nets, ngpus, best, 4); + } + #endif + for (i = 0; i < divs*divs; ++i) { + printf("%.2f ", resized.y.vals[0][train.y.cols + i]); + if((i+1)%divs == 0) printf("\n"); + free_data(tiles[i]); + } + free_data(best); + printf("\n"); + image im = float_to_image(64,64,3,resized.X.vals[0]); + //show_image(im, "orig"); + //cvWaitKey(100); + /* + image im1 = float_to_image(64,64,3,tiles[i].X.vals[0]); + image im2 = float_to_image(64,64,3,resized.X.vals[0]); + show_image(im1, "tile"); + show_image(im2, "res"); + */ +#ifdef GPU + if (ngpus == 1) { + aloss = train_network(net, resized); + } else { + aloss = train_networks(nets, ngpus, resized, 4); + } +#endif + for(i = 0; i < divs*divs; ++i){ + printf("%f ", nets[0]->output[1000 + i]); + if ((i+1) % divs == 0) printf("\n"); + } + printf("\n"); + + free_data(resized); + free_data(train); + if(avg_cls_loss == -1) avg_cls_loss = closs; + if(avg_att_loss == -1) avg_att_loss = aloss; + avg_cls_loss = avg_cls_loss*.9 + closs*.1; + avg_att_loss = avg_att_loss*.9 + aloss*.1; + + printf("%ld, %.3f: Att: %f, %f avg, Class: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net->seen)/N, aloss, avg_att_loss, closs, avg_cls_loss, get_current_rate(net), what_time_is_it_now()-time, *net->seen); + if(*net->seen/N > epoch){ + epoch = *net->seen/N; + char buff[256]; + sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); + save_weights(net, buff); + } + if(get_current_batch(net)%1000 == 0){ + char buff[256]; + sprintf(buff, "%s/%s.backup",backup_directory,base); + save_weights(net, buff); + } + } + char buff[256]; + sprintf(buff, "%s/%s.weights", backup_directory, base); + save_weights(net, buff); + pthread_join(load_thread, 0); + + free_network(net); + free_ptrs((void**)labels, classes); + free_ptrs((void**)paths, plist->size); + free_list(plist); + free(base); +} + +void validate_attention_single(char *datacfg, char *filename, char *weightfile) +{ + int i, j; + network *net = load_network(filename, weightfile, 0); + set_batch_network(net, 1); + srand(time(0)); + + list *options = read_data_cfg(datacfg); + + char *label_list = option_find_str(options, "labels", "data/labels.list"); + char *leaf_list = option_find_str(options, "leaves", 0); + if(leaf_list) change_leaves(net->hierarchy, leaf_list); + char *valid_list = option_find_str(options, "valid", "data/train.list"); + int classes = option_find_int(options, "classes", 2); + int topk = option_find_int(options, "top", 1); + + char **labels = get_labels(label_list); + list *plist = get_paths(valid_list); + + char **paths = (char **)list_to_array(plist); + int m = plist->size; + free_list(plist); + + float avg_acc = 0; + float avg_topk = 0; + int *indexes = calloc(topk, sizeof(int)); + int divs = 4; + int size = 2; + int extra = 0; + float *avgs = calloc(classes, sizeof(float)); + int *inds = calloc(divs*divs, sizeof(int)); + + for(i = 0; i < m; ++i){ + int class = -1; + char *path = paths[i]; + for(j = 0; j < classes; ++j){ + if(strstr(path, labels[j])){ + class = j; + break; + } + } + image im = load_image_color(paths[i], 0, 0); + image resized = resize_min(im, net->w*divs/size); + image crop = crop_image(resized, (resized.w - net->w*divs/size)/2, (resized.h - net->h*divs/size)/2, net->w*divs/size, net->h*divs/size); + image rcrop = resize_image(crop, net->w, net->h); + //show_image(im, "orig"); + //show_image(crop, "cropped"); + //cvWaitKey(0); + float *pred = network_predict(net, rcrop.data); + //pred[classes + 56] = 0; + for(j = 0; j < divs*divs; ++j){ + printf("%.2f ", pred[classes + j]); + if((j+1)%divs == 0) printf("\n"); + } + printf("\n"); + copy_cpu(classes, pred, 1, avgs, 1); + top_k(pred + classes, divs*divs, divs*divs, inds); + show_image(crop, "crop"); + for(j = 0; j < extra; ++j){ + int index = inds[j]; + int row = index / divs; + int col = index % divs; + int y = row * crop.h / divs - (net->h - crop.h/divs)/2; + int x = col * crop.w / divs - (net->w - crop.w/divs)/2; + printf("%d %d %d %d\n", row, col, y, x); + image tile = crop_image(crop, x, y, net->w, net->h); + float *pred = network_predict(net, tile.data); + axpy_cpu(classes, 1., pred, 1, avgs, 1); + show_image(tile, "tile"); + //cvWaitKey(10); + } + if(net->hierarchy) hierarchy_predictions(pred, net->outputs, net->hierarchy, 1, 1); + + if(rcrop.data != resized.data) free_image(rcrop); + if(resized.data != im.data) free_image(resized); + free_image(im); + free_image(crop); + top_k(pred, classes, topk, indexes); + + if(indexes[0] == class) avg_acc += 1; + for(j = 0; j < topk; ++j){ + if(indexes[j] == class) avg_topk += 1; + } + + printf("%d: top 1: %f, top %d: %f\n", i, avg_acc/(i+1), topk, avg_topk/(i+1)); + } +} + +void validate_attention_multi(char *datacfg, char *filename, char *weightfile) +{ + int i, j; + network *net = load_network(filename, weightfile, 0); + set_batch_network(net, 1); + srand(time(0)); + + list *options = read_data_cfg(datacfg); + + char *label_list = option_find_str(options, "labels", "data/labels.list"); + char *valid_list = option_find_str(options, "valid", "data/train.list"); + int classes = option_find_int(options, "classes", 2); + int topk = option_find_int(options, "top", 1); + + char **labels = get_labels(label_list); + list *plist = get_paths(valid_list); + int scales[] = {224, 288, 320, 352, 384}; + int nscales = sizeof(scales)/sizeof(scales[0]); + + char **paths = (char **)list_to_array(plist); + int m = plist->size; + free_list(plist); + + float avg_acc = 0; + float avg_topk = 0; + int *indexes = calloc(topk, sizeof(int)); + + for(i = 0; i < m; ++i){ + int class = -1; + char *path = paths[i]; + for(j = 0; j < classes; ++j){ + if(strstr(path, labels[j])){ + class = j; + break; + } + } + float *pred = calloc(classes, sizeof(float)); + image im = load_image_color(paths[i], 0, 0); + for(j = 0; j < nscales; ++j){ + image r = resize_min(im, scales[j]); + resize_network(net, r.w, r.h); + float *p = network_predict(net, r.data); + if(net->hierarchy) hierarchy_predictions(p, net->outputs, net->hierarchy, 1 , 1); + axpy_cpu(classes, 1, p, 1, pred, 1); + flip_image(r); + p = network_predict(net, r.data); + axpy_cpu(classes, 1, p, 1, pred, 1); + if(r.data != im.data) free_image(r); + } + free_image(im); + top_k(pred, classes, topk, indexes); + free(pred); + if(indexes[0] == class) avg_acc += 1; + for(j = 0; j < topk; ++j){ + if(indexes[j] == class) avg_topk += 1; + } + + printf("%d: top 1: %f, top %d: %f\n", i, avg_acc/(i+1), topk, avg_topk/(i+1)); + } +} + +void predict_attention(char *datacfg, char *cfgfile, char *weightfile, char *filename, int top) +{ + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + srand(2222222); + + list *options = read_data_cfg(datacfg); + + char *name_list = option_find_str(options, "names", 0); + if(!name_list) name_list = option_find_str(options, "labels", "data/labels.list"); + if(top == 0) top = option_find_int(options, "top", 1); + + int i = 0; + char **names = get_labels(name_list); + clock_t time; + int *indexes = calloc(top, sizeof(int)); + char buff[256]; + char *input = buff; + 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, "\n"); + } + image im = load_image_color(input, 0, 0); + image r = letterbox_image(im, net->w, net->h); + //resize_network(&net, r.w, r.h); + //printf("%d %d\n", r.w, r.h); + + float *X = r.data; + time=clock(); + float *predictions = network_predict(net, X); + if(net->hierarchy) hierarchy_predictions(predictions, net->outputs, net->hierarchy, 1, 1); + top_k(predictions, net->outputs, top, indexes); + fprintf(stderr, "%s: Predicted in %f seconds.\n", input, sec(clock()-time)); + for(i = 0; i < top; ++i){ + int index = indexes[i]; + //if(net->hierarchy) printf("%d, %s: %f, parent: %s \n",index, names[index], predictions[index], (net->hierarchy->parent[index] >= 0) ? names[net->hierarchy->parent[index]] : "Root"); + //else printf("%s: %f\n",names[index], predictions[index]); + printf("%5.2f%%: %s\n", predictions[index]*100, names[index]); + } + if(r.data != im.data) free_image(r); + free_image(im); + if (filename) break; + } +} + + +void run_attention(int argc, char **argv) +{ + if(argc < 4){ + fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); + return; + } + + char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); + int ngpus; + int *gpus = read_intlist(gpu_list, &ngpus, gpu_index); + + + int top = find_int_arg(argc, argv, "-t", 0); + int clear = find_arg(argc, argv, "-clear"); + char *data = argv[3]; + char *cfg = argv[4]; + char *weights = (argc > 5) ? argv[5] : 0; + char *filename = (argc > 6) ? argv[6]: 0; + char *layer_s = (argc > 7) ? argv[7]: 0; + if(0==strcmp(argv[2], "predict")) predict_attention(data, cfg, weights, filename, top); + else if(0==strcmp(argv[2], "train")) train_attention(data, cfg, weights, gpus, ngpus, clear); + else if(0==strcmp(argv[2], "valid")) validate_attention_single(data, cfg, weights); + else if(0==strcmp(argv[2], "validmulti")) validate_attention_multi(data, cfg, weights); +} + + diff --git a/image.darknet/inst/include/darknet/src/captcha.c b/image.darknet/inst/include/darknet/examples/captcha.c similarity index 88% rename from image.darknet/inst/include/darknet/src/captcha.c rename to image.darknet/inst/include/darknet/examples/captcha.c index 3d449b2..41d6d07 100644 --- a/image.darknet/inst/include/darknet/src/captcha.c +++ b/image.darknet/inst/include/darknet/examples/captcha.c @@ -1,6 +1,4 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" +#include "darknet.h" void fix_data_captcha(data d, int mask) { @@ -32,13 +30,10 @@ void train_captcha(char *cfgfile, char *weightfile) float avg_loss = -1; char *base = basecfg(cfgfile); printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + network *net = load_network(cfgfile, weightfile, 0); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); int imgs = 1024; - int i = *net.seen/imgs; + int i = *net->seen/imgs; int solved = 1; list *plist; char **labels = get_labels("/data/captcha/reimgs.labels.list"); @@ -55,8 +50,8 @@ void train_captcha(char *cfgfile, char *weightfile) data buffer; load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; args.paths = paths; args.classes = 26; args.n = imgs; @@ -85,7 +80,7 @@ void train_captcha(char *cfgfile, char *weightfile) float loss = train_network(net, train); if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), *net.seen); + printf("%d: %f, %f avg, %lf seconds, %ld images\n", i, loss, avg_loss, sec(clock()-time), *net->seen); free_data(train); if(i%100==0){ char buff[256]; @@ -97,11 +92,8 @@ void train_captcha(char *cfgfile, char *weightfile) void test_captcha(char *cfgfile, char *weightfile, char *filename) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); srand(2222222); int i = 0; char **names = get_labels("/data/captcha/reimgs.labels.list"); @@ -118,7 +110,7 @@ void test_captcha(char *cfgfile, char *weightfile, char *filename) if(!input) return; strtok(input, "\n"); } - image im = load_image_color(input, net.w, net.h); + image im = load_image_color(input, net->w, net->h); float *X = im.data; float *predictions = network_predict(net, X); top_predictions(net, 26, indexes); @@ -138,21 +130,18 @@ void test_captcha(char *cfgfile, char *weightfile, char *filename) void valid_captcha(char *cfgfile, char *weightfile, char *filename) { char **labels = get_labels("/data/captcha/reimgs.labels.list"); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, 0); list *plist = get_paths("/data/captcha/reimgs.fg.list"); char **paths = (char **)list_to_array(plist); int N = plist->size; - int outputs = net.outputs; + int outputs = net->outputs; - set_batch_network(&net, 1); + set_batch_network(net, 1); srand(2222222); int i, j; for(i = 0; i < N; ++i){ if (i%100 == 0) fprintf(stderr, "%d\n", i); - image im = load_image_color(paths[i], net.w, net.h); + image im = load_image_color(paths[i], net->w, net->h); float *X = im.data; float *predictions = network_predict(net, X); //printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); @@ -187,9 +176,9 @@ void valid_captcha(char *cfgfile, char *weightfile, char *filename) if(weightfile){ load_weights(&net, weightfile); } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); int imgs = 1024; - int i = net.seen/imgs; + int i = net->seen/imgs; list *plist = get_paths("/data/captcha/train.auto5"); char **paths = (char **)list_to_array(plist); printf("%d\n", plist->size); @@ -203,10 +192,10 @@ void valid_captcha(char *cfgfile, char *weightfile, char *filename) printf("Loaded: %lf seconds\n", sec(clock()-time)); time=clock(); float loss = train_network(net, train); - net.seen += imgs; + net->seen += imgs; if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), net.seen); + printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), net->seen); free_data(train); if(i%10==0){ char buff[256]; @@ -253,9 +242,9 @@ network net = parse_network_cfg(cfgfile); if(weightfile){ load_weights(&net, weightfile); } -printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); +printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); int imgs = 1024; -int i = net.seen/imgs; +int i = net->seen/imgs; list *plist = get_paths("/data/captcha/encode.list"); char **paths = (char **)list_to_array(plist); printf("%d\n", plist->size); @@ -268,10 +257,10 @@ while(1){ printf("Loaded: %lf seconds\n", sec(clock()-time)); time=clock(); float loss = train_network(net, train); - net.seen += imgs; + net->seen += imgs; if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), net.seen); + printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), net->seen); free_matrix(train.X); if(i%100==0){ char buff[256]; diff --git a/image.darknet/inst/include/darknet/src/cifar.c b/image.darknet/inst/include/darknet/examples/cifar.c similarity index 77% rename from image.darknet/inst/include/darknet/src/cifar.c rename to image.darknet/inst/include/darknet/examples/cifar.c index d0ac459..a5f5f24 100644 --- a/image.darknet/inst/include/darknet/src/cifar.c +++ b/image.darknet/inst/include/darknet/examples/cifar.c @@ -1,12 +1,4 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" -#include "option_list.h" -#include "blas.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif +#include "darknet.h" void train_cifar(char *cfgfile, char *weightfile) { @@ -14,28 +6,25 @@ void train_cifar(char *cfgfile, char *weightfile) float avg_loss = -1; char *base = basecfg(cfgfile); printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + network *net = load_network(cfgfile, weightfile, 0); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); char *backup_directory = "/home/pjreddie/backup/"; int classes = 10; int N = 50000; char **labels = get_labels("data/cifar/labels.txt"); - int epoch = (*net.seen)/N; + int epoch = (*net->seen)/N; data train = load_all_cifar10(); - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ + while(get_current_batch(net) < net->max_batches || net->max_batches == 0){ clock_t time=clock(); float loss = train_network_sgd(net, train, 1); if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.95 + loss*.05; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; + printf("%ld, %.3f: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net->seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net->seen); + if(*net->seen/N > epoch){ + epoch = *net->seen/N; char buff[256]; sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); save_weights(net, buff); @@ -62,18 +51,15 @@ void train_cifar_distill(char *cfgfile, char *weightfile) float avg_loss = -1; char *base = basecfg(cfgfile); printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + network *net = load_network(cfgfile, weightfile, 0); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); char *backup_directory = "/home/pjreddie/backup/"; int classes = 10; int N = 50000; char **labels = get_labels("data/cifar/labels.txt"); - int epoch = (*net.seen)/N; + int epoch = (*net->seen)/N; data train = load_all_cifar10(); matrix soft = csv_to_matrix("results/ensemble.csv"); @@ -83,15 +69,15 @@ void train_cifar_distill(char *cfgfile, char *weightfile) scale_matrix(train.y, 1. - weight); matrix_add_matrix(soft, train.y); - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ + while(get_current_batch(net) < net->max_batches || net->max_batches == 0){ clock_t time=clock(); float loss = train_network_sgd(net, train, 1); if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.95 + loss*.05; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; + printf("%ld, %.3f: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net->seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net->seen); + if(*net->seen/N > epoch){ + epoch = *net->seen/N; char buff[256]; sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); save_weights(net, buff); @@ -114,11 +100,8 @@ void train_cifar_distill(char *cfgfile, char *weightfile) void test_cifar_multi(char *filename, char *weightfile) { - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(filename, weightfile, 0); + set_batch_network(net, 1); srand(time(0)); float avg_acc = 0; @@ -146,10 +129,7 @@ void test_cifar_multi(char *filename, char *weightfile) void test_cifar(char *filename, char *weightfile) { - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(filename, weightfile, 0); srand(time(0)); clock_t time; @@ -177,23 +157,20 @@ char *labels[] = {"airplane","automobile","bird","cat","deer","dog","frog","hors int class = max_index(train.y.vals[i], 10); char buff[256]; sprintf(buff, "data/cifar/train/%d_%s",i,labels[class]); - save_image_png(im, buff); + save_image_options(im, buff, PNG, 0); } for(i = 0; i < test.X.rows; ++i){ image im = float_to_image(32, 32, 3, test.X.vals[i]); int class = max_index(test.y.vals[i], 10); char buff[256]; sprintf(buff, "data/cifar/test/%d_%s",i,labels[class]); - save_image_png(im, buff); + save_image_options(im, buff, PNG, 0); } } void test_cifar_csv(char *filename, char *weightfile) { - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(filename, weightfile, 0); srand(time(0)); data test = load_cifar10_data("data/cifar/cifar-10-batches-bin/test_batch.bin"); @@ -215,12 +192,9 @@ void test_cifar_csv(char *filename, char *weightfile) free_data(test); } -void test_cifar_csvtrain(char *filename, char *weightfile) +void test_cifar_csvtrain(char *cfg, char *weights) { - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(cfg, weights, 0); srand(time(0)); data test = load_all_cifar10(); diff --git a/image.darknet/inst/include/darknet/src/classifier.c b/image.darknet/inst/include/darknet/examples/classifier.c similarity index 72% rename from image.darknet/inst/include/darknet/src/classifier.c rename to image.darknet/inst/include/darknet/examples/classifier.c index 586530a..df91a08 100644 --- a/image.darknet/inst/include/darknet/src/classifier.c +++ b/image.darknet/inst/include/darknet/examples/classifier.c @@ -1,17 +1,7 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" -#include "option_list.h" -#include "blas.h" -#include "assert.h" -#include "classifier.h" -#include "cuda.h" -#include +#include "darknet.h" -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -image get_image_from_stream(CvCapture *cap); -#endif +#include +#include float *get_regression_values(char **labels, int n) { @@ -33,7 +23,7 @@ void train_classifier(char *datacfg, char *cfgfile, char *weightfile, int *gpus, char *base = basecfg(cfgfile); printf("%s\n", base); printf("%d\n", ngpus); - network *nets = calloc(ngpus, sizeof(network)); + network **nets = calloc(ngpus, sizeof(network*)); srand(time(0)); int seed = rand(); @@ -42,54 +32,61 @@ void train_classifier(char *datacfg, char *cfgfile, char *weightfile, int *gpus, #ifdef GPU cuda_set_device(gpus[i]); #endif - nets[i] = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&nets[i], weightfile); - } - if(clear) *nets[i].seen = 0; - nets[i].learning_rate *= ngpus; + nets[i] = load_network(cfgfile, weightfile, clear); + nets[i]->learning_rate *= ngpus; } srand(time(0)); - network net = nets[0]; + network *net = nets[0]; - int imgs = net.batch * net.subdivisions * ngpus; + int imgs = net->batch * net->subdivisions * ngpus; - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); list *options = read_data_cfg(datacfg); char *backup_directory = option_find_str(options, "backup", "/backup/"); + int tag = option_find_int_quiet(options, "tag", 0); char *label_list = option_find_str(options, "labels", "data/labels.list"); char *train_list = option_find_str(options, "train", "data/train.list"); + char *tree = option_find_str(options, "tree", 0); + if (tree) net->hierarchy = read_tree(tree); int classes = option_find_int(options, "classes", 2); - char **labels = get_labels(label_list); + char **labels = 0; + if(!tag){ + labels = get_labels(label_list); + } list *plist = get_paths(train_list); char **paths = (char **)list_to_array(plist); printf("%d\n", plist->size); int N = plist->size; - clock_t time; + double time; load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; args.threads = 32; - args.hierarchy = net.hierarchy; - - args.min = net.min_crop; - args.max = net.max_crop; - args.angle = net.angle; - args.aspect = net.aspect; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; - args.size = net.w; + args.hierarchy = net->hierarchy; + + args.min = net->min_ratio*net->w; + args.max = net->max_ratio*net->w; + printf("%d %d\n", args.min, args.max); + args.angle = net->angle; + args.aspect = net->aspect; + args.exposure = net->exposure; + args.saturation = net->saturation; + args.hue = net->hue; + args.size = net->w; args.paths = paths; args.classes = classes; args.n = imgs; args.m = N; args.labels = labels; - args.type = CLASSIFICATION_DATA; + if (tag){ + args.type = TAG_DATA; + } else { + args.type = CLASSIFICATION_DATA; + } data train; data buffer; @@ -97,16 +94,40 @@ void train_classifier(char *datacfg, char *cfgfile, char *weightfile, int *gpus, args.d = &buffer; load_thread = load_data(args); - int epoch = (*net.seen)/N; - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - time=clock(); + int count = 0; + int epoch = (*net->seen)/N; + while(get_current_batch(net) < net->max_batches || net->max_batches == 0){ + if(net->random && count++%40 == 0){ + printf("Resizing\n"); + int dim = (rand() % 11 + 4) * 32; + //if (get_current_batch(net)+200 > net->max_batches) dim = 608; + //int dim = (rand() % 4 + 16) * 32; + printf("%d\n", dim); + args.w = dim; + args.h = dim; + args.size = dim; + args.min = net->min_ratio*dim; + args.max = net->max_ratio*dim; + printf("%d %d\n", args.min, args.max); + + pthread_join(load_thread, 0); + train = buffer; + free_data(train); + load_thread = load_data(args); + + for(i = 0; i < ngpus; ++i){ + resize_network(nets[i], dim, dim); + } + net = nets[0]; + } + time = what_time_is_it_now(); pthread_join(load_thread, 0); train = buffer; load_thread = load_data(args); - printf("Loaded: %lf seconds\n", sec(clock()-time)); - time=clock(); + printf("Loaded: %lf seconds\n", what_time_is_it_now()-time); + time = what_time_is_it_now(); float loss = 0; #ifdef GPU @@ -120,15 +141,15 @@ void train_classifier(char *datacfg, char *cfgfile, char *weightfile, int *gpus, #endif if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); + printf("%ld, %.3f: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net->seen)/N, loss, avg_loss, get_current_rate(net), what_time_is_it_now()-time, *net->seen); free_data(train); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; + if(*net->seen/N > epoch){ + epoch = *net->seen/N; char buff[256]; sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); save_weights(net, buff); } - if(get_current_batch(net)%100 == 0){ + if(get_current_batch(net)%1000 == 0){ char buff[256]; sprintf(buff, "%s/%s.backup",backup_directory,base); save_weights(net, buff); @@ -137,132 +158,19 @@ void train_classifier(char *datacfg, char *cfgfile, char *weightfile, int *gpus, char buff[256]; sprintf(buff, "%s/%s.weights", backup_directory, base); save_weights(net, buff); + pthread_join(load_thread, 0); free_network(net); - free_ptrs((void**)labels, classes); + if(labels) free_ptrs((void**)labels, classes); free_ptrs((void**)paths, plist->size); free_list(plist); free(base); } - -/* - void train_classifier(char *datacfg, char *cfgfile, char *weightfile, int clear) - { - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - if(clear) *net.seen = 0; - - int imgs = net.batch * net.subdivisions; - - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - list *options = read_data_cfg(datacfg); - - char *backup_directory = option_find_str(options, "backup", "/backup/"); - char *label_list = option_find_str(options, "labels", "data/labels.list"); - char *train_list = option_find_str(options, "train", "data/train.list"); - int classes = option_find_int(options, "classes", 2); - - char **labels = get_labels(label_list); - list *plist = get_paths(train_list); - char **paths = (char **)list_to_array(plist); - printf("%d\n", plist->size); - int N = plist->size; - clock_t time; - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.threads = 8; - - args.min = net.min_crop; - args.max = net.max_crop; - args.angle = net.angle; - args.aspect = net.aspect; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; - args.size = net.w; - args.hierarchy = net.hierarchy; - - args.paths = paths; - args.classes = classes; - args.n = imgs; - args.m = N; - args.labels = labels; - args.type = CLASSIFICATION_DATA; - - data train; - data buffer; - pthread_t load_thread; - args.d = &buffer; - load_thread = load_data(args); - - int epoch = (*net.seen)/N; - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - time=clock(); - - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data(args); - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - time=clock(); - -#ifdef OPENCV -if(0){ -int u; -for(u = 0; u < imgs; ++u){ - image im = float_to_image(net.w, net.h, 3, train.X.vals[u]); - show_image(im, "loaded"); - cvWaitKey(0); -} -} -#endif - -float loss = train_network(net, train); -free_data(train); - -if(avg_loss == -1) avg_loss = loss; -avg_loss = avg_loss*.9 + loss*.1; -printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); -if(*net.seen/N > epoch){ - epoch = *net.seen/N; - char buff[256]; - sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); - save_weights(net, buff); -} -if(get_current_batch(net)%100 == 0){ - char buff[256]; - sprintf(buff, "%s/%s.backup",backup_directory,base); - save_weights(net, buff); -} -} -char buff[256]; -sprintf(buff, "%s/%s.weights", backup_directory, base); -save_weights(net, buff); - -free_network(net); -free_ptrs((void**)labels, classes); -free_ptrs((void**)paths, plist->size); -free_list(plist); -free(base); -} -*/ - void validate_classifier_crop(char *datacfg, char *filename, char *weightfile) { int i = 0; - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(filename, weightfile, 0); srand(time(0)); list *options = read_data_cfg(datacfg); @@ -288,8 +196,8 @@ void validate_classifier_crop(char *datacfg, char *filename, char *weightfile) data val, buffer; load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; args.paths = paths; args.classes = classes; @@ -326,11 +234,8 @@ void validate_classifier_crop(char *datacfg, char *filename, char *weightfile) void validate_classifier_10(char *datacfg, char *filename, char *weightfile) { int i, j; - network net = parse_network_cfg(filename); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(filename, weightfile, 0); + set_batch_network(net, 1); srand(time(0)); list *options = read_data_cfg(datacfg); @@ -360,8 +265,8 @@ void validate_classifier_10(char *datacfg, char *filename, char *weightfile) break; } } - int w = net.w; - int h = net.h; + int w = net->w; + int h = net->h; int shift = 32; image im = load_image_color(paths[i], w+shift, h+shift); image images[10]; @@ -379,7 +284,7 @@ void validate_classifier_10(char *datacfg, char *filename, char *weightfile) float *pred = calloc(classes, sizeof(float)); for(j = 0; j < 10; ++j){ float *p = network_predict(net, images[j].data); - if(net.hierarchy) hierarchy_predictions(p, net.outputs, net.hierarchy, 1); + if(net->hierarchy) hierarchy_predictions(p, net->outputs, net->hierarchy, 1, 1); axpy_cpu(classes, 1, p, 1, pred, 1); free_image(images[j]); } @@ -398,11 +303,8 @@ void validate_classifier_10(char *datacfg, char *filename, char *weightfile) void validate_classifier_full(char *datacfg, char *filename, char *weightfile) { int i, j; - network net = parse_network_cfg(filename); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(filename, weightfile, 0); + set_batch_network(net, 1); srand(time(0)); list *options = read_data_cfg(datacfg); @@ -423,7 +325,7 @@ void validate_classifier_full(char *datacfg, char *filename, char *weightfile) float avg_topk = 0; int *indexes = calloc(topk, sizeof(int)); - int size = net.w; + int size = net->w; for(i = 0; i < m; ++i){ int class = -1; char *path = paths[i]; @@ -435,12 +337,12 @@ void validate_classifier_full(char *datacfg, char *filename, char *weightfile) } image im = load_image_color(paths[i], 0, 0); image resized = resize_min(im, size); - resize_network(&net, resized.w, resized.h); + resize_network(net, resized.w, resized.h); //show_image(im, "orig"); //show_image(crop, "cropped"); //cvWaitKey(0); float *pred = network_predict(net, resized.data); - if(net.hierarchy) hierarchy_predictions(pred, net.outputs, net.hierarchy, 1); + if(net->hierarchy) hierarchy_predictions(pred, net->outputs, net->hierarchy, 1, 1); free_image(im); free_image(resized); @@ -459,18 +361,15 @@ void validate_classifier_full(char *datacfg, char *filename, char *weightfile) void validate_classifier_single(char *datacfg, char *filename, char *weightfile) { int i, j; - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(filename, weightfile, 0); + set_batch_network(net, 1); srand(time(0)); list *options = read_data_cfg(datacfg); char *label_list = option_find_str(options, "labels", "data/labels.list"); char *leaf_list = option_find_str(options, "leaves", 0); - if(leaf_list) change_leaves(net.hierarchy, leaf_list); + if(leaf_list) change_leaves(net->hierarchy, leaf_list); char *valid_list = option_find_str(options, "valid", "data/train.list"); int classes = option_find_int(options, "classes", 2); int topk = option_find_int(options, "top", 1); @@ -496,15 +395,14 @@ void validate_classifier_single(char *datacfg, char *filename, char *weightfile) } } image im = load_image_color(paths[i], 0, 0); - image resized = resize_min(im, net.w); - image crop = crop_image(resized, (resized.w - net.w)/2, (resized.h - net.h)/2, net.w, net.h); + image crop = center_crop_image(im, net->w, net->h); + //grayscale_image_3c(crop); //show_image(im, "orig"); //show_image(crop, "cropped"); //cvWaitKey(0); float *pred = network_predict(net, crop.data); - if(net.hierarchy) hierarchy_predictions(pred, net.outputs, net.hierarchy, 1); + if(net->hierarchy) hierarchy_predictions(pred, net->outputs, net->hierarchy, 1, 1); - if(resized.data != im.data) free_image(resized); free_image(im); free_image(crop); top_k(pred, classes, topk, indexes); @@ -514,18 +412,16 @@ void validate_classifier_single(char *datacfg, char *filename, char *weightfile) if(indexes[j] == class) avg_topk += 1; } + printf("%s, %d, %f, %f, \n", paths[i], class, pred[0], pred[1]); printf("%d: top 1: %f, top %d: %f\n", i, avg_acc/(i+1), topk, avg_topk/(i+1)); } } -void validate_classifier_multi(char *datacfg, char *filename, char *weightfile) +void validate_classifier_multi(char *datacfg, char *cfg, char *weights) { int i, j; - network net = parse_network_cfg(filename); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); srand(time(0)); list *options = read_data_cfg(datacfg); @@ -537,7 +433,8 @@ void validate_classifier_multi(char *datacfg, char *filename, char *weightfile) char **labels = get_labels(label_list); list *plist = get_paths(valid_list); - int scales[] = {224, 288, 320, 352, 384}; + //int scales[] = {224, 288, 320, 352, 384}; + int scales[] = {224, 256, 288, 320}; int nscales = sizeof(scales)/sizeof(scales[0]); char **paths = (char **)list_to_array(plist); @@ -560,10 +457,10 @@ void validate_classifier_multi(char *datacfg, char *filename, char *weightfile) float *pred = calloc(classes, sizeof(float)); image im = load_image_color(paths[i], 0, 0); for(j = 0; j < nscales; ++j){ - image r = resize_min(im, scales[j]); - resize_network(&net, r.w, r.h); + image r = resize_max(im, scales[j]); + resize_network(net, r.w, r.h); float *p = network_predict(net, r.data); - if(net.hierarchy) hierarchy_predictions(p, net.outputs, net.hierarchy, 1); + if(net->hierarchy) hierarchy_predictions(p, net->outputs, net->hierarchy, 1 , 1); axpy_cpu(classes, 1, p, 1, pred, 1); flip_image(r); p = network_predict(net, r.data); @@ -584,11 +481,8 @@ void validate_classifier_multi(char *datacfg, char *filename, char *weightfile) void try_classifier(char *datacfg, char *cfgfile, char *weightfile, char *filename, int layer_num) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); srand(2222222); list *options = read_data_cfg(datacfg); @@ -629,7 +523,7 @@ void try_classifier(char *datacfg, char *cfgfile, char *weightfile, char *filena time=clock(); float *predictions = network_predict(net, X); - layer l = net.layers[layer_num]; + layer l = net->layers[layer_num]; for(i = 0; i < l.c; ++i){ if(l.rolling_mean) printf("%f %f %f\n", l.rolling_mean[i], l.rolling_variance[i], l.scales[i]); } @@ -665,11 +559,8 @@ void try_classifier(char *datacfg, char *cfgfile, char *weightfile, char *filena void predict_classifier(char *datacfg, char *cfgfile, char *weightfile, char *filename, int top) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); srand(2222222); list *options = read_data_cfg(datacfg); @@ -684,7 +575,6 @@ void predict_classifier(char *datacfg, char *cfgfile, char *weightfile, char *fi int *indexes = calloc(top, sizeof(int)); char buff[256]; char *input = buff; - int size = net.w; while(1){ if(filename){ strncpy(input, filename, 256); @@ -696,20 +586,23 @@ void predict_classifier(char *datacfg, char *cfgfile, char *weightfile, char *fi strtok(input, "\n"); } image im = load_image_color(input, 0, 0); - image r = resize_min(im, size); - resize_network(&net, r.w, r.h); - printf("%d %d\n", r.w, r.h); + image r = letterbox_image(im, net->w, net->h); + //image r = resize_min(im, 320); + //printf("%d %d\n", r.w, r.h); + //resize_network(net, r.w, r.h); + //printf("%d %d\n", r.w, r.h); float *X = r.data; time=clock(); float *predictions = network_predict(net, X); - if(net.hierarchy) hierarchy_predictions(predictions, net.outputs, net.hierarchy, 0); - top_k(predictions, net.outputs, top, indexes); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); + if(net->hierarchy) hierarchy_predictions(predictions, net->outputs, net->hierarchy, 1, 1); + top_k(predictions, net->outputs, top, indexes); + fprintf(stderr, "%s: Predicted in %f seconds.\n", input, sec(clock()-time)); for(i = 0; i < top; ++i){ int index = indexes[i]; - if(net.hierarchy) printf("%d, %s: %f, parent: %s \n",index, names[index], predictions[index], (net.hierarchy->parent[index] >= 0) ? names[net.hierarchy->parent[index]] : "Root"); - else printf("%s: %f\n",names[index], predictions[index]); + //if(net->hierarchy) printf("%d, %s: %f, parent: %s \n",index, names[index], predictions[index], (net->hierarchy->parent[index] >= 0) ? names[net->hierarchy->parent[index]] : "Root"); + //else printf("%s: %f\n",names[index], predictions[index]); + printf("%5.2f%%: %s\n", predictions[index]*100, names[index]); } if(r.data != im.data) free_image(r); free_image(im); @@ -721,11 +614,8 @@ void predict_classifier(char *datacfg, char *cfgfile, char *weightfile, char *fi void label_classifier(char *datacfg, char *filename, char *weightfile) { int i; - network net = parse_network_cfg(filename); - set_batch_network(&net, 1); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(filename, weightfile, 0); + set_batch_network(net, 1); srand(time(0)); list *options = read_data_cfg(datacfg); @@ -743,8 +633,8 @@ void label_classifier(char *datacfg, char *filename, char *weightfile) for(i = 0; i < m; ++i){ image im = load_image_color(paths[i], 0, 0); - image resized = resize_min(im, net.w); - image crop = crop_image(resized, (resized.w - net.w)/2, (resized.h - net.h)/2, net.w, net.h); + image resized = resize_min(im, net->w); + image crop = crop_image(resized, (resized.w - net->w)/2, (resized.h - net->h)/2, net->w, net->h); float *pred = network_predict(net, crop.data); if(resized.data != im.data) free_image(resized); @@ -756,14 +646,50 @@ void label_classifier(char *datacfg, char *filename, char *weightfile) } } +void csv_classifier(char *datacfg, char *cfgfile, char *weightfile) +{ + int i,j; + network *net = load_network(cfgfile, weightfile, 0); + srand(time(0)); + + list *options = read_data_cfg(datacfg); + + char *test_list = option_find_str(options, "test", "data/test.list"); + int top = option_find_int(options, "top", 1); + + list *plist = get_paths(test_list); + + char **paths = (char **)list_to_array(plist); + int m = plist->size; + free_list(plist); + int *indexes = calloc(top, sizeof(int)); + + for(i = 0; i < m; ++i){ + double time = what_time_is_it_now(); + char *path = paths[i]; + image im = load_image_color(path, 0, 0); + image r = letterbox_image(im, net->w, net->h); + float *predictions = network_predict(net, r.data); + if(net->hierarchy) hierarchy_predictions(predictions, net->outputs, net->hierarchy, 1, 1); + top_k(predictions, net->outputs, top, indexes); + + printf("%s", path); + for(j = 0; j < top; ++j){ + printf("\t%d", indexes[j]); + } + printf("\n"); + + free_image(im); + free_image(r); + + fprintf(stderr, "%lf seconds, %d images, %d total\n", what_time_is_it_now() - time, i+1, m); + } +} void test_classifier(char *datacfg, char *cfgfile, char *weightfile, int target_layer) { int curr = 0; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, 0); srand(time(0)); list *options = read_data_cfg(datacfg); @@ -782,18 +708,18 @@ void test_classifier(char *datacfg, char *cfgfile, char *weightfile, int target_ data val, buffer; load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; args.paths = paths; args.classes = classes; - args.n = net.batch; + args.n = net->batch; args.m = 0; args.labels = 0; args.d = &buffer; args.type = OLD_CLASSIFICATION_DATA; pthread_t load_thread = load_data_in_thread(args); - for(curr = net.batch; curr < m; curr += net.batch){ + for(curr = net->batch; curr < m; curr += net->batch){ time=clock(); pthread_join(load_thread, 0); @@ -801,7 +727,7 @@ void test_classifier(char *datacfg, char *cfgfile, char *weightfile, int target_ if(curr < m){ args.paths = paths + curr; - if (curr + net.batch > m) args.n = m - curr; + if (curr + net->batch > m) args.n = m - curr; load_thread = load_data_in_thread(args); } fprintf(stderr, "Loaded: %d images in %lf seconds\n", val.X.rows, sec(clock()-time)); @@ -811,11 +737,11 @@ void test_classifier(char *datacfg, char *cfgfile, char *weightfile, int target_ int i, j; if (target_layer >= 0){ - //layer l = net.layers[target_layer]; + //layer l = net->layers[target_layer]; } for(i = 0; i < pred.rows; ++i){ - printf("%s", paths[curr-net.batch+i]); + printf("%s", paths[curr-net->batch+i]); for(j = 0; j < pred.cols; ++j){ printf("\t%g", pred.vals[i][j]); } @@ -829,6 +755,44 @@ void test_classifier(char *datacfg, char *cfgfile, char *weightfile, int target_ } } +void file_output_classifier(char *datacfg, char *filename, char *weightfile, char *listfile) +{ + int i,j; + network *net = load_network(filename, weightfile, 0); + set_batch_network(net, 1); + srand(time(0)); + + list *options = read_data_cfg(datacfg); + + //char *label_list = option_find_str(options, "names", "data/labels.list"); + int classes = option_find_int(options, "classes", 2); + + list *plist = get_paths(listfile); + + char **paths = (char **)list_to_array(plist); + int m = plist->size; + free_list(plist); + + for(i = 0; i < m; ++i){ + image im = load_image_color(paths[i], 0, 0); + image resized = resize_min(im, net->w); + image crop = crop_image(resized, (resized.w - net->w)/2, (resized.h - net->h)/2, net->w, net->h); + + float *pred = network_predict(net, crop.data); + if(net->hierarchy) hierarchy_predictions(pred, net->outputs, net->hierarchy, 0, 1); + + if(resized.data != im.data) free_image(resized); + free_image(im); + free_image(crop); + + printf("%s", paths[i]); + for(j = 0; j < classes; ++j){ + printf("\t%g", pred[j]); + } + printf("\n"); + } +} + void threat_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_index, const char *filename) { @@ -837,21 +801,12 @@ void threat_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_i float roll = .2; printf("Classifier Demo\n"); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); list *options = read_data_cfg(datacfg); srand(2222222); - CvCapture * cap; - - if(filename){ - cap = cvCaptureFromFile(filename); - }else{ - cap = cvCaptureFromCAM(cam_index); - } + void * cap = open_video_stream(filename, cam_index, 0,0,0); int top = option_find_int(options, "top", 1); @@ -875,7 +830,7 @@ void threat_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_i image in = get_image_from_stream(cap); if(!in.data) break; - image in_s = resize_image(in, net.w, net.h); + image in_s = resize_image(in, net->w, net->h); image out = in; int x1 = out.w / 20; @@ -948,8 +903,7 @@ void threat_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_i } if(1){ - show_image(out, "Threat"); - cvWaitKey(10); + show_image(out, "Threat", 10); } free_image(in_s); free_image(in); @@ -969,21 +923,12 @@ void gun_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_inde int bad_cats[] = {218, 539, 540, 1213, 1501, 1742, 1911, 2415, 4348, 19223, 368, 369, 370, 1133, 1200, 1306, 2122, 2301, 2537, 2823, 3179, 3596, 3639, 4489, 5107, 5140, 5289, 6240, 6631, 6762, 7048, 7171, 7969, 7984, 7989, 8824, 8927, 9915, 10270, 10448, 13401, 15205, 18358, 18894, 18895, 19249, 19697}; printf("Classifier Demo\n"); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); list *options = read_data_cfg(datacfg); srand(2222222); - CvCapture * cap; - - if(filename){ - cap = cvCaptureFromFile(filename); - }else{ - cap = cvCaptureFromCAM(cam_index); - } + void * cap = open_video_stream(filename, cam_index, 0,0,0); int top = option_find_int(options, "top", 1); @@ -993,8 +938,6 @@ void gun_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_inde int *indexes = calloc(top, sizeof(int)); if(!cap) error("Couldn't connect to webcam.\n"); - cvNamedWindow("Threat Detection", CV_WINDOW_NORMAL); - cvResizeWindow("Threat Detection", 512, 512); float fps = 0; int i; @@ -1003,8 +946,7 @@ void gun_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_inde gettimeofday(&tval_before, NULL); image in = get_image_from_stream(cap); - image in_s = resize_image(in, net.w, net.h); - show_image(in, "Threat Detection"); + image in_s = resize_image(in, net->w, net->h); float *predictions = network_predict(net, in_s.data); top_predictions(net, top, indexes); @@ -1029,11 +971,10 @@ void gun_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_inde } } + show_image(in, "Threat Detection", 10); free_image(in_s); free_image(in); - cvWaitKey(10); - gettimeofday(&tval_after, NULL); timersub(&tval_after, &tval_before, &tval_result); float curr = 1000000.f/((long int)tval_result.tv_usec); @@ -1045,33 +986,28 @@ void gun_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_inde void demo_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_index, const char *filename) { #ifdef OPENCV + char *base = basecfg(cfgfile); + image **alphabet = load_alphabet(); printf("Classifier Demo\n"); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); list *options = read_data_cfg(datacfg); srand(2222222); - CvCapture * cap; - if(filename){ - cap = cvCaptureFromFile(filename); - }else{ - cap = cvCaptureFromCAM(cam_index); - } + int w = 1280; + int h = 720; + void * cap = open_video_stream(filename, cam_index, w, h, 0); int top = option_find_int(options, "top", 1); - char *name_list = option_find_str(options, "names", 0); + char *label_list = option_find_str(options, "labels", 0); + char *name_list = option_find_str(options, "names", label_list); char **names = get_labels(name_list); int *indexes = calloc(top, sizeof(int)); if(!cap) error("Couldn't connect to webcam.\n"); - cvNamedWindow("Classifier", CV_WINDOW_NORMAL); - cvResizeWindow("Classifier", 512, 512); float fps = 0; int i; @@ -1080,27 +1016,38 @@ void demo_classifier(char *datacfg, char *cfgfile, char *weightfile, int cam_ind gettimeofday(&tval_before, NULL); image in = get_image_from_stream(cap); - image in_s = resize_image(in, net.w, net.h); - show_image(in, "Classifier"); + //image in_s = resize_image(in, net->w, net->h); + image in_s = letterbox_image(in, net->w, net->h); float *predictions = network_predict(net, in_s.data); - if(net.hierarchy) hierarchy_predictions(predictions, net.outputs, net.hierarchy, 1); + if(net->hierarchy) hierarchy_predictions(predictions, net->outputs, net->hierarchy, 1, 1); top_predictions(net, top, indexes); printf("\033[2J"); printf("\033[1;1H"); printf("\nFPS:%.0f\n",fps); + int lh = in.h*.03; + int toph = 3*lh; + + float rgb[3] = {1,1,1}; for(i = 0; i < top; ++i){ + printf("%d\n", toph); int index = indexes[i]; printf("%.1f%%: %s\n", predictions[index]*100, names[index]); + + char buff[1024]; + sprintf(buff, "%3.1f%%: %s\n", predictions[index]*100, names[index]); + image label = get_label(alphabet, buff, lh); + draw_label(in, toph, lh, label, rgb); + toph += 2*lh; + free_image(label); } + show_image(in, base, 10); free_image(in_s); free_image(in); - cvWaitKey(10); - gettimeofday(&tval_after, NULL); timersub(&tval_after, &tval_before, &tval_result); float curr = 1000000.f/((long int)tval_result.tv_usec); @@ -1118,27 +1065,9 @@ void run_classifier(int argc, char **argv) } char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); - int *gpus = 0; - int gpu = 0; - int ngpus = 0; - if(gpu_list){ - printf("%s\n", gpu_list); - int len = strlen(gpu_list); - ngpus = 1; - int i; - for(i = 0; i < len; ++i){ - if (gpu_list[i] == ',') ++ngpus; - } - gpus = calloc(ngpus, sizeof(int)); - for(i = 0; i < ngpus; ++i){ - gpus[i] = atoi(gpu_list); - gpu_list = strchr(gpu_list, ',')+1; - } - } else { - gpu = gpu_index; - gpus = &gpu; - ngpus = 1; - } + int ngpus; + int *gpus = read_intlist(gpu_list, &ngpus, gpu_index); + int cam_index = find_int_arg(argc, argv, "-c", 0); int top = find_int_arg(argc, argv, "-t", 0); @@ -1150,12 +1079,14 @@ void run_classifier(int argc, char **argv) char *layer_s = (argc > 7) ? argv[7]: 0; int layer = layer_s ? atoi(layer_s) : -1; if(0==strcmp(argv[2], "predict")) predict_classifier(data, cfg, weights, filename, top); + else if(0==strcmp(argv[2], "fout")) file_output_classifier(data, cfg, weights, filename); else if(0==strcmp(argv[2], "try")) try_classifier(data, cfg, weights, filename, atoi(layer_s)); else if(0==strcmp(argv[2], "train")) train_classifier(data, cfg, weights, gpus, ngpus, clear); else if(0==strcmp(argv[2], "demo")) demo_classifier(data, cfg, weights, cam_index, filename); else if(0==strcmp(argv[2], "gun")) gun_classifier(data, cfg, weights, cam_index, filename); else if(0==strcmp(argv[2], "threat")) threat_classifier(data, cfg, weights, cam_index, filename); else if(0==strcmp(argv[2], "test")) test_classifier(data, cfg, weights, layer); + else if(0==strcmp(argv[2], "csv")) csv_classifier(data, cfg, weights); else if(0==strcmp(argv[2], "label")) label_classifier(data, cfg, weights); else if(0==strcmp(argv[2], "valid")) validate_classifier_single(data, cfg, weights); else if(0==strcmp(argv[2], "validmulti")) validate_classifier_multi(data, cfg, weights); diff --git a/image.darknet/inst/include/darknet/src/coco.c b/image.darknet/inst/include/darknet/examples/coco.c similarity index 72% rename from image.darknet/inst/include/darknet/src/coco.c rename to image.darknet/inst/include/darknet/examples/coco.c index 8f3c968..6a50b89 100644 --- a/image.darknet/inst/include/darknet/src/coco.c +++ b/image.darknet/inst/include/darknet/examples/coco.c @@ -1,16 +1,6 @@ -#include - -#include "network.h" -#include "detection_layer.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "box.h" -#include "demo.h" +#include "darknet.h" -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif +#include char *coco_classes[] = {"person","bicycle","car","motorcycle","airplane","bus","train","truck","boat","traffic light","fire hydrant","stop sign","parking meter","bench","bird","cat","dog","horse","sheep","cow","elephant","bear","zebra","giraffe","backpack","umbrella","handbag","tie","suitcase","frisbee","skis","snowboard","sports ball","kite","baseball bat","baseball glove","skateboard","surfboard","tennis racket","bottle","wine glass","cup","fork","knife","spoon","bowl","banana","apple","sandwich","orange","broccoli","carrot","hot dog","pizza","donut","cake","chair","couch","potted plant","bed","dining table","toilet","tv","laptop","mouse","remote","keyboard","cell phone","microwave","oven","toaster","sink","refrigerator","book","clock","vase","scissors","teddy bear","hair drier","toothbrush"}; @@ -27,17 +17,14 @@ void train_coco(char *cfgfile, char *weightfile) char *base = basecfg(cfgfile); printf("%s\n", base); float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - int i = *net.seen/imgs; + network *net = load_network(cfgfile, weightfile, 0); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + int imgs = net->batch*net->subdivisions; + int i = *net->seen/imgs; data train, buffer; - layer l = net.layers[net.n - 1]; + layer l = net->layers[net->n - 1]; int side = l.side; int classes = l.classes; @@ -48,8 +35,8 @@ void train_coco(char *cfgfile, char *weightfile) char **paths = (char **)list_to_array(plist); load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; args.paths = paths; args.n = imgs; args.m = plist->size; @@ -59,15 +46,15 @@ void train_coco(char *cfgfile, char *weightfile) args.d = &buffer; args.type = REGION_DATA; - args.angle = net.angle; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; + args.angle = net->angle; + args.exposure = net->exposure; + args.saturation = net->saturation; + args.hue = net->hue; pthread_t load_thread = load_data_in_thread(args); clock_t time; //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ + while(get_current_batch(net) < net->max_batches){ i += 1; time=clock(); pthread_join(load_thread, 0); @@ -77,7 +64,7 @@ void train_coco(char *cfgfile, char *weightfile) printf("Loaded: %lf seconds\n", sec(clock()-time)); /* - image im = float_to_image(net.w, net.h, 3, train.X.vals[113]); + image im = float_to_image(net->w, net->h, 3, train.X.vals[113]); image copy = copy_image(im); draw_coco(copy, train.y.vals[113], 7, "truth"); cvWaitKey(0); @@ -107,14 +94,14 @@ void train_coco(char *cfgfile, char *weightfile) save_weights(net, buff); } -void print_cocos(FILE *fp, int image_id, box *boxes, float **probs, int num_boxes, int classes, int w, int h) +static void print_cocos(FILE *fp, int image_id, detection *dets, int num_boxes, int classes, int w, int h) { int i, j; for(i = 0; i < num_boxes; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; + float xmin = dets[i].bbox.x - dets[i].bbox.w/2.; + float xmax = dets[i].bbox.x + dets[i].bbox.w/2.; + float ymin = dets[i].bbox.y - dets[i].bbox.h/2.; + float ymax = dets[i].bbox.y + dets[i].bbox.h/2.; if (xmin < 0) xmin = 0; if (ymin < 0) ymin = 0; @@ -127,7 +114,7 @@ void print_cocos(FILE *fp, int image_id, box *boxes, float **probs, int num_boxe float bh = ymax - ymin; for(j = 0; j < classes; ++j){ - if (probs[i][j]) fprintf(fp, "{\"image_id\":%d, \"category_id\":%d, \"bbox\":[%f, %f, %f, %f], \"score\":%f},\n", image_id, coco_ids[j], bx, by, bw, bh, probs[i][j]); + if (dets[i].prob[j]) fprintf(fp, "{\"image_id\":%d, \"category_id\":%d, \"bbox\":[%f, %f, %f, %f], \"score\":%f},\n", image_id, coco_ids[j], bx, by, bw, bh, dets[i].prob[j]); } } } @@ -138,14 +125,11 @@ int get_coco_image_id(char *filename) return atoi(p+1); } -void validate_coco(char *cfgfile, char *weightfile) +void validate_coco(char *cfg, char *weights) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); + fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); srand(time(0)); char *base = "results/"; @@ -154,20 +138,14 @@ void validate_coco(char *cfgfile, char *weightfile) //list *plist = get_paths("/home/pjreddie/data/voc/test/2007_test.txt"); char **paths = (char **)list_to_array(plist); - layer l = net.layers[net.n-1]; + layer l = net->layers[net->n-1]; int classes = l.classes; - int side = l.side; - int j; char buff[1024]; snprintf(buff, 1024, "%s/coco_results.json", base); FILE *fp = fopen(buff, "w"); fprintf(fp, "[\n"); - box *boxes = calloc(side*side*l.n, sizeof(box)); - float **probs = calloc(side*side*l.n, sizeof(float *)); - for(j = 0; j < side*side*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); - int m = plist->size; int i=0; int t; @@ -184,8 +162,8 @@ void validate_coco(char *cfgfile, char *weightfile) pthread_t *thr = calloc(nthreads, sizeof(pthread_t)); load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; args.type = IMAGE_DATA; for(t = 0; t < nthreads; ++t){ @@ -215,9 +193,11 @@ void validate_coco(char *cfgfile, char *weightfile) network_predict(net, X); int w = val[t].w; int h = val[t].h; - get_detection_boxes(l, w, h, thresh, probs, boxes, 0); - if (nms) do_nms_sort(boxes, probs, side*side*l.n, classes, iou_thresh); - print_cocos(fp, image_id, boxes, probs, side*side*l.n, classes, w, h); + int nboxes = 0; + detection *dets = get_network_boxes(net, w, h, thresh, 0, 0, 0, &nboxes); + if (nms) do_nms_sort(dets, l.side*l.side*l.n, classes, iou_thresh); + print_cocos(fp, image_id, dets, l.side*l.side*l.n, classes, w, h); + free_detections(dets, nboxes); free_image(val[t]); free_image(val_resized[t]); } @@ -231,19 +211,16 @@ void validate_coco(char *cfgfile, char *weightfile) void validate_coco_recall(char *cfgfile, char *weightfile) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); srand(time(0)); char *base = "results/comp4_det_test_"; list *plist = get_paths("/home/pjreddie/data/voc/test/2007_test.txt"); char **paths = (char **)list_to_array(plist); - layer l = net.layers[net.n-1]; + layer l = net->layers[net->n-1]; int classes = l.classes; int side = l.side; @@ -254,9 +231,6 @@ void validate_coco_recall(char *cfgfile, char *weightfile) snprintf(buff, 1024, "%s%s.txt", base, coco_classes[j]); fps[j] = fopen(buff, "w"); } - box *boxes = calloc(side*side*l.n, sizeof(box)); - float **probs = calloc(side*side*l.n, sizeof(float *)); - for(j = 0; j < side*side*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); int m = plist->size; int i=0; @@ -264,7 +238,6 @@ void validate_coco_recall(char *cfgfile, char *weightfile) float thresh = .001; int nms = 0; float iou_thresh = .5; - float nms_thresh = .5; int total = 0; int correct = 0; @@ -274,11 +247,13 @@ void validate_coco_recall(char *cfgfile, char *weightfile) for(i = 0; i < m; ++i){ char *path = paths[i]; image orig = load_image_color(path, 0, 0); - image sized = resize_image(orig, net.w, net.h); + image sized = resize_image(orig, net->w, net->h); char *id = basecfg(path); network_predict(net, sized.data); - get_detection_boxes(l, 1, 1, thresh, probs, boxes, 1); - if (nms) do_nms(boxes, probs, side*side*l.n, 1, nms_thresh); + + int nboxes = 0; + detection *dets = get_network_boxes(net, orig.w, orig.h, thresh, 0, 0, 1, &nboxes); + if (nms) do_nms_obj(dets, side*side*l.n, 1, nms); char labelpath[4096]; find_replace(path, "images", "labels", labelpath); @@ -289,7 +264,7 @@ void validate_coco_recall(char *cfgfile, char *weightfile) int num_labels = 0; box_label *truth = read_boxes(labelpath, &num_labels); for(k = 0; k < side*side*l.n; ++k){ - if(probs[k][0] > thresh){ + if(dets[k].objectness > thresh){ ++proposals; } } @@ -298,8 +273,8 @@ void validate_coco_recall(char *cfgfile, char *weightfile) box t = {truth[j].x, truth[j].y, truth[j].w, truth[j].h}; float best_iou = 0; for(k = 0; k < side*side*l.n; ++k){ - float iou = box_iou(boxes[k], t); - if(probs[k][0] > thresh && iou > best_iou){ + float iou = box_iou(dets[k].bbox, t); + if(dets[k].objectness > thresh && iou > best_iou){ best_iou = iou; } } @@ -308,7 +283,7 @@ void validate_coco_recall(char *cfgfile, char *weightfile) ++correct; } } - + free_detections(dets, nboxes); fprintf(stderr, "%5d %5d %5d\tRPs/Img: %.2f\tIOU: %.2f%%\tRecall:%.2f%%\n", i, correct, total, (float)proposals/(i+1), avg_iou*100/total, 100.*correct/total); free(id); free_image(orig); @@ -319,21 +294,14 @@ void validate_coco_recall(char *cfgfile, char *weightfile) void test_coco(char *cfgfile, char *weightfile, char *filename, float thresh) { image **alphabet = load_alphabet(); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - detection_layer l = net.layers[net.n-1]; - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + layer l = net->layers[net->n-1]; + set_batch_network(net, 1); srand(2222222); float nms = .4; clock_t time; char buff[256]; char *input = buff; - int j; - box *boxes = calloc(l.side*l.side*l.n, sizeof(box)); - float **probs = calloc(l.side*l.side*l.n, sizeof(float *)); - for(j = 0; j < l.side*l.side*l.n; ++j) probs[j] = calloc(l.classes, sizeof(float *)); while(1){ if(filename){ strncpy(input, filename, 256); @@ -345,22 +313,22 @@ void test_coco(char *cfgfile, char *weightfile, char *filename, float thresh) strtok(input, "\n"); } image im = load_image_color(input,0,0); - image sized = resize_image(im, net.w, net.h); + image sized = resize_image(im, net->w, net->h); float *X = sized.data; time=clock(); network_predict(net, X); printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - get_detection_boxes(l, 1, 1, thresh, probs, boxes, 0); - if (nms) do_nms_sort(boxes, probs, l.side*l.side*l.n, l.classes, nms); - draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, coco_classes, alphabet, 80); + + int nboxes = 0; + detection *dets = get_network_boxes(net, 1, 1, thresh, 0, 0, 0, &nboxes); + if (nms) do_nms_sort(dets, l.side*l.side*l.n, l.classes, nms); + + draw_detections(im, dets, l.side*l.side*l.n, thresh, coco_classes, alphabet, 80); save_image(im, "prediction"); - show_image(im, "predictions"); + show_image(im, "predictions", 0); + free_detections(dets, nboxes); free_image(im); free_image(sized); -#ifdef OPENCV - cvWaitKey(0); - cvDestroyAllWindows(); -#endif if (filename) break; } } @@ -380,9 +348,10 @@ void run_coco(int argc, char **argv) char *cfg = argv[3]; char *weights = (argc > 4) ? argv[4] : 0; char *filename = (argc > 5) ? argv[5]: 0; + int avg = find_int_arg(argc, argv, "-avg", 1); if(0==strcmp(argv[2], "test")) test_coco(cfg, weights, filename, thresh); else if(0==strcmp(argv[2], "train")) train_coco(cfg, weights); else if(0==strcmp(argv[2], "valid")) validate_coco(cfg, weights); else if(0==strcmp(argv[2], "recall")) validate_coco_recall(cfg, weights); - else if(0==strcmp(argv[2], "demo")) demo(cfg, weights, thresh, cam_index, filename, coco_classes, 80, frame_skip, prefix, .5); + else if(0==strcmp(argv[2], "demo")) demo(cfg, weights, thresh, cam_index, filename, coco_classes, 80, frame_skip, prefix, avg, .5, 0,0,0,0); } diff --git a/image.darknet/inst/include/darknet/src/darknet.c b/image.darknet/inst/include/darknet/examples/darknet.c similarity index 62% rename from image.darknet/inst/include/darknet/src/darknet.c rename to image.darknet/inst/include/darknet/examples/darknet.c index 6e56072..d538359 100644 --- a/image.darknet/inst/include/darknet/src/darknet.c +++ b/image.darknet/inst/include/darknet/examples/darknet.c @@ -1,56 +1,46 @@ +#include "darknet.h" + #include #include #include -#include "parser.h" -#include "utils.h" -#include "cuda.h" -#include "blas.h" -#include "connected_layer.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - extern void predict_classifier(char *datacfg, char *cfgfile, char *weightfile, char *filename, int top); -extern void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh); -extern void run_voxel(int argc, char **argv); +extern void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen); extern void run_yolo(int argc, char **argv); extern void run_detector(int argc, char **argv); extern void run_coco(int argc, char **argv); -extern void run_writing(int argc, char **argv); -extern void run_captcha(int argc, char **argv); extern void run_nightmare(int argc, char **argv); -extern void run_dice(int argc, char **argv); -extern void run_compare(int argc, char **argv); extern void run_classifier(int argc, char **argv); +extern void run_regressor(int argc, char **argv); +extern void run_segmenter(int argc, char **argv); +extern void run_isegmenter(int argc, char **argv); extern void run_char_rnn(int argc, char **argv); -extern void run_vid_rnn(int argc, char **argv); extern void run_tag(int argc, char **argv); extern void run_cifar(int argc, char **argv); extern void run_go(int argc, char **argv); extern void run_art(int argc, char **argv); extern void run_super(int argc, char **argv); +extern void run_lsd(int argc, char **argv); void average(int argc, char *argv[]) { char *cfgfile = argv[2]; char *outfile = argv[3]; gpu_index = -1; - network net = parse_network_cfg(cfgfile); - network sum = parse_network_cfg(cfgfile); + network *net = parse_network_cfg(cfgfile); + network *sum = parse_network_cfg(cfgfile); char *weightfile = argv[4]; - load_weights(&sum, weightfile); + load_weights(sum, weightfile); int i, j; int n = argc - 5; for(i = 0; i < n; ++i){ weightfile = argv[i+5]; - load_weights(&net, weightfile); - for(j = 0; j < net.n; ++j){ - layer l = net.layers[j]; - layer out = sum.layers[j]; + load_weights(net, weightfile); + for(j = 0; j < net->n; ++j){ + layer l = net->layers[j]; + layer out = sum->layers[j]; if(l.type == CONVOLUTIONAL){ int num = l.n*l.c*l.size*l.size; axpy_cpu(l.n, 1, l.biases, 1, out.biases, 1); @@ -68,8 +58,8 @@ void average(int argc, char *argv[]) } } n = n+1; - for(j = 0; j < net.n; ++j){ - layer l = sum.layers[j]; + for(j = 0; j < net->n; ++j){ + layer l = sum->layers[j]; if(l.type == CONVOLUTIONAL){ int num = l.n*l.c*l.size*l.size; scal_cpu(l.n, 1./n, l.biases, 1); @@ -88,19 +78,57 @@ void average(int argc, char *argv[]) save_weights(sum, outfile); } +long numops(network *net) +{ + int i; + long ops = 0; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; + if(l.type == CONVOLUTIONAL){ + ops += 2l * l.n * l.size*l.size*l.c/l.groups * l.out_h*l.out_w; + } else if(l.type == CONNECTED){ + ops += 2l * l.inputs * l.outputs; + } else if (l.type == RNN){ + ops += 2l * l.input_layer->inputs * l.input_layer->outputs; + ops += 2l * l.self_layer->inputs * l.self_layer->outputs; + ops += 2l * l.output_layer->inputs * l.output_layer->outputs; + } else if (l.type == GRU){ + ops += 2l * l.uz->inputs * l.uz->outputs; + ops += 2l * l.uh->inputs * l.uh->outputs; + ops += 2l * l.ur->inputs * l.ur->outputs; + ops += 2l * l.wz->inputs * l.wz->outputs; + ops += 2l * l.wh->inputs * l.wh->outputs; + ops += 2l * l.wr->inputs * l.wr->outputs; + } else if (l.type == LSTM){ + ops += 2l * l.uf->inputs * l.uf->outputs; + ops += 2l * l.ui->inputs * l.ui->outputs; + ops += 2l * l.ug->inputs * l.ug->outputs; + ops += 2l * l.uo->inputs * l.uo->outputs; + ops += 2l * l.wf->inputs * l.wf->outputs; + ops += 2l * l.wi->inputs * l.wi->outputs; + ops += 2l * l.wg->inputs * l.wg->outputs; + ops += 2l * l.wo->inputs * l.wo->outputs; + } + } + return ops; +} + void speed(char *cfgfile, int tics) { if (tics == 0) tics = 1000; - network net = parse_network_cfg(cfgfile); - set_batch_network(&net, 1); + network *net = parse_network_cfg(cfgfile); + set_batch_network(net, 1); int i; - time_t start = time(0); - image im = make_image(net.w, net.h, net.c); + double time=what_time_is_it_now(); + image im = make_image(net->w, net->h, net->c*net->batch); for(i = 0; i < tics; ++i){ network_predict(net, im.data); } - double t = difftime(time(0), start); + double t = what_time_is_it_now() - time; + long ops = numops(net); printf("\n%d evals, %f Seconds\n", tics, t); + printf("Floating Point Operations: %.2f Bn\n", (float)ops/1000000000.); + printf("FLOPS: %.2f Bn\n", (float)ops/1000000000.*tics/t); printf("Speed: %f sec/eval\n", t/tics); printf("Speed: %f Hz\n", tics/t); } @@ -108,17 +136,8 @@ void speed(char *cfgfile, int tics) void operations(char *cfgfile) { gpu_index = -1; - network net = parse_network_cfg(cfgfile); - int i; - long ops = 0; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; - if(l.type == CONVOLUTIONAL){ - ops += 2l * l.n * l.size*l.size*l.c * l.out_h*l.out_w; - } else if(l.type == CONNECTED){ - ops += 2l * l.inputs * l.outputs; - } - } + network *net = parse_network_cfg(cfgfile); + long ops = numops(net); printf("Floating Point Operations: %ld\n", ops); printf("Floating Point Operations: %.2f Bn\n", (float)ops/1000000000.); } @@ -126,52 +145,75 @@ void operations(char *cfgfile) void oneoff(char *cfgfile, char *weightfile, char *outfile) { gpu_index = -1; - network net = parse_network_cfg(cfgfile); - int oldn = net.layers[net.n - 2].n; - int c = net.layers[net.n - 2].c; - scal_cpu(oldn*c, .1, net.layers[net.n - 2].weights, 1); - scal_cpu(oldn, 0, net.layers[net.n - 2].biases, 1); - net.layers[net.n - 2].n = 9418; - net.layers[net.n - 2].biases += 5; - net.layers[net.n - 2].weights += 5*c; + network *net = parse_network_cfg(cfgfile); + int oldn = net->layers[net->n - 2].n; + int c = net->layers[net->n - 2].c; + scal_cpu(oldn*c, .1, net->layers[net->n - 2].weights, 1); + scal_cpu(oldn, 0, net->layers[net->n - 2].biases, 1); + net->layers[net->n - 2].n = 11921; + net->layers[net->n - 2].biases += 5; + net->layers[net->n - 2].weights += 5*c; if(weightfile){ - load_weights(&net, weightfile); + load_weights(net, weightfile); } - net.layers[net.n - 2].biases -= 5; - net.layers[net.n - 2].weights -= 5*c; - net.layers[net.n - 2].n = oldn; + net->layers[net->n - 2].biases -= 5; + net->layers[net->n - 2].weights -= 5*c; + net->layers[net->n - 2].n = oldn; printf("%d\n", oldn); - layer l = net.layers[net.n - 2]; + layer l = net->layers[net->n - 2]; copy_cpu(l.n/3, l.biases, 1, l.biases + l.n/3, 1); copy_cpu(l.n/3, l.biases, 1, l.biases + 2*l.n/3, 1); copy_cpu(l.n/3*l.c, l.weights, 1, l.weights + l.n/3*l.c, 1); copy_cpu(l.n/3*l.c, l.weights, 1, l.weights + 2*l.n/3*l.c, 1); - *net.seen = 0; + *net->seen = 0; save_weights(net, outfile); } -void partial(char *cfgfile, char *weightfile, char *outfile, int max) +void oneoff2(char *cfgfile, char *weightfile, char *outfile, int l) { gpu_index = -1; - network net = parse_network_cfg(cfgfile); + network *net = parse_network_cfg(cfgfile); if(weightfile){ - load_weights_upto(&net, weightfile, max); + load_weights_upto(net, weightfile, 0, net->n); + load_weights_upto(net, weightfile, l, net->n); } - *net.seen = 0; + *net->seen = 0; + save_weights_upto(net, outfile, net->n); +} + +void partial(char *cfgfile, char *weightfile, char *outfile, int max) +{ + gpu_index = -1; + network *net = load_network(cfgfile, weightfile, 1); save_weights_upto(net, outfile, max); } -#include "convolutional_layer.h" -void rescale_net(char *cfgfile, char *weightfile, char *outfile) +void print_weights(char *cfgfile, char *weightfile, int n) { gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); + network *net = load_network(cfgfile, weightfile, 1); + layer l = net->layers[n]; + int i, j; + //printf("["); + for(i = 0; i < l.n; ++i){ + //printf("["); + for(j = 0; j < l.size*l.size*l.c; ++j){ + //if(j > 0) printf(","); + printf("%g ", l.weights[i*l.size*l.size*l.c + j]); + } + printf("\n"); + //printf("]%s\n", (i == l.n-1)?"":","); } + //printf("]"); +} + +void rescale_net(char *cfgfile, char *weightfile, char *outfile) +{ + gpu_index = -1; + network *net = load_network(cfgfile, weightfile, 0); int i; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; if(l.type == CONVOLUTIONAL){ rescale_weights(l, 2, -.5); break; @@ -183,13 +225,10 @@ void rescale_net(char *cfgfile, char *weightfile, char *outfile) void rgbgr_net(char *cfgfile, char *weightfile, char *outfile) { gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, 0); int i; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; if(l.type == CONVOLUTIONAL){ rgbgr_weights(l); break; @@ -201,13 +240,10 @@ void rgbgr_net(char *cfgfile, char *weightfile, char *outfile) void reset_normalize_net(char *cfgfile, char *weightfile, char *outfile) { gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if (weightfile) { - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, 0); int i; - for (i = 0; i < net.n; ++i) { - layer l = net.layers[i]; + for (i = 0; i < net->n; ++i) { + layer l = net->layers[i]; if (l.type == CONVOLUTIONAL && l.batch_normalize) { denormalize_convolutional_layer(l); } @@ -242,18 +278,15 @@ layer normalize_layer(layer l, int n) void normalize_net(char *cfgfile, char *weightfile, char *outfile) { gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, 0); int i; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; if(l.type == CONVOLUTIONAL && !l.batch_normalize){ - net.layers[i] = normalize_layer(l, l.n); + net->layers[i] = normalize_layer(l, l.n); } if (l.type == CONNECTED && !l.batch_normalize) { - net.layers[i] = normalize_layer(l, l.outputs); + net->layers[i] = normalize_layer(l, l.outputs); } if (l.type == GRU && l.batch_normalize) { *l.input_z_layer = normalize_layer(*l.input_z_layer, l.input_z_layer->outputs); @@ -262,7 +295,7 @@ void normalize_net(char *cfgfile, char *weightfile, char *outfile) *l.state_z_layer = normalize_layer(*l.state_z_layer, l.state_z_layer->outputs); *l.state_r_layer = normalize_layer(*l.state_r_layer, l.state_r_layer->outputs); *l.state_h_layer = normalize_layer(*l.state_h_layer, l.state_h_layer->outputs); - net.layers[i].batch_normalize=1; + net->layers[i].batch_normalize=1; } } save_weights(net, outfile); @@ -271,13 +304,10 @@ void normalize_net(char *cfgfile, char *weightfile, char *outfile) void statistics_net(char *cfgfile, char *weightfile) { gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if (weightfile) { - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, 0); int i; - for (i = 0; i < net.n; ++i) { - layer l = net.layers[i]; + for (i = 0; i < net->n; ++i) { + layer l = net->layers[i]; if (l.type == CONNECTED && l.batch_normalize) { printf("Connected Layer %d\n", i); statistics_connected_layer(l); @@ -304,20 +334,17 @@ void statistics_net(char *cfgfile, char *weightfile) void denormalize_net(char *cfgfile, char *weightfile, char *outfile) { gpu_index = -1; - network net = parse_network_cfg(cfgfile); - if (weightfile) { - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, 0); int i; - for (i = 0; i < net.n; ++i) { - layer l = net.layers[i]; - if (l.type == CONVOLUTIONAL && l.batch_normalize) { + for (i = 0; i < net->n; ++i) { + layer l = net->layers[i]; + if ((l.type == DECONVOLUTIONAL || l.type == CONVOLUTIONAL) && l.batch_normalize) { denormalize_convolutional_layer(l); - net.layers[i].batch_normalize=0; + net->layers[i].batch_normalize=0; } if (l.type == CONNECTED && l.batch_normalize) { denormalize_connected_layer(l); - net.layers[i].batch_normalize=0; + net->layers[i].batch_normalize=0; } if (l.type == GRU && l.batch_normalize) { denormalize_connected_layer(*l.input_z_layer); @@ -332,22 +359,42 @@ void denormalize_net(char *cfgfile, char *weightfile, char *outfile) l.state_z_layer->batch_normalize = 0; l.state_r_layer->batch_normalize = 0; l.state_h_layer->batch_normalize = 0; - net.layers[i].batch_normalize=0; + net->layers[i].batch_normalize=0; } } save_weights(net, outfile); } -void visualize(char *cfgfile, char *weightfile) +void mkimg(char *cfgfile, char *weightfile, int h, int w, int num, char *prefix) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); + network *net = load_network(cfgfile, weightfile, 0); + image *ims = get_weights(net->layers[0]); + int n = net->layers[0].n; + int z; + for(z = 0; z < num; ++z){ + image im = make_image(h, w, 3); + fill_image(im, .5); + int i; + for(i = 0; i < 100; ++i){ + image r = copy_image(ims[rand()%n]); + rotate_image_cw(r, rand()%4); + random_distort_image(r, 1, 1.5, 1.5); + int dx = rand()%(w-r.w); + int dy = rand()%(h-r.h); + ghost_image(r, im, dx, dy); + free_image(r); + } + char buff[256]; + sprintf(buff, "%s/gen_%d", prefix, z); + save_image(im, buff); + free_image(im); } +} + +void visualize(char *cfgfile, char *weightfile) +{ + network *net = load_network(cfgfile, weightfile, 0); visualize_network(net); -#ifdef OPENCV - cvWaitKey(0); -#endif } int main(int argc, char **argv) @@ -376,46 +423,44 @@ int main(int argc, char **argv) average(argc, argv); } else if (0 == strcmp(argv[1], "yolo")){ run_yolo(argc, argv); - } else if (0 == strcmp(argv[1], "voxel")){ - run_voxel(argc, argv); } else if (0 == strcmp(argv[1], "super")){ run_super(argc, argv); + } else if (0 == strcmp(argv[1], "lsd")){ + run_lsd(argc, argv); } else if (0 == strcmp(argv[1], "detector")){ run_detector(argc, argv); } else if (0 == strcmp(argv[1], "detect")){ - float thresh = find_float_arg(argc, argv, "-thresh", .24); + float thresh = find_float_arg(argc, argv, "-thresh", .5); char *filename = (argc > 4) ? argv[4]: 0; - test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5); + char *outfile = find_char_arg(argc, argv, "-out", 0); + int fullscreen = find_arg(argc, argv, "-fullscreen"); + test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5, outfile, fullscreen); } else if (0 == strcmp(argv[1], "cifar")){ run_cifar(argc, argv); } else if (0 == strcmp(argv[1], "go")){ run_go(argc, argv); } else if (0 == strcmp(argv[1], "rnn")){ run_char_rnn(argc, argv); - } else if (0 == strcmp(argv[1], "vid")){ - run_vid_rnn(argc, argv); } else if (0 == strcmp(argv[1], "coco")){ run_coco(argc, argv); } else if (0 == strcmp(argv[1], "classify")){ predict_classifier("cfg/imagenet1k.data", argv[2], argv[3], argv[4], 5); } else if (0 == strcmp(argv[1], "classifier")){ run_classifier(argc, argv); + } else if (0 == strcmp(argv[1], "regressor")){ + run_regressor(argc, argv); + } else if (0 == strcmp(argv[1], "isegmenter")){ + run_isegmenter(argc, argv); + } else if (0 == strcmp(argv[1], "segmenter")){ + run_segmenter(argc, argv); } else if (0 == strcmp(argv[1], "art")){ run_art(argc, argv); } else if (0 == strcmp(argv[1], "tag")){ run_tag(argc, argv); - } else if (0 == strcmp(argv[1], "compare")){ - run_compare(argc, argv); - } else if (0 == strcmp(argv[1], "dice")){ - run_dice(argc, argv); - } else if (0 == strcmp(argv[1], "writing")){ - run_writing(argc, argv); } else if (0 == strcmp(argv[1], "3d")){ composite_3d(argv[2], argv[3], argv[4], (argc > 5) ? atof(argv[5]) : 0); } else if (0 == strcmp(argv[1], "test")){ test_resize(argv[2]); - } else if (0 == strcmp(argv[1], "captcha")){ - run_captcha(argc, argv); } else if (0 == strcmp(argv[1], "nightmare")){ run_nightmare(argc, argv); } else if (0 == strcmp(argv[1], "rgbgr")){ @@ -436,12 +481,18 @@ int main(int argc, char **argv) speed(argv[2], (argc > 3 && argv[3]) ? atoi(argv[3]) : 0); } else if (0 == strcmp(argv[1], "oneoff")){ oneoff(argv[2], argv[3], argv[4]); + } else if (0 == strcmp(argv[1], "oneoff2")){ + oneoff2(argv[2], argv[3], argv[4], atoi(argv[5])); + } else if (0 == strcmp(argv[1], "print")){ + print_weights(argv[2], argv[3], atoi(argv[4])); } else if (0 == strcmp(argv[1], "partial")){ partial(argv[2], argv[3], argv[4], atoi(argv[5])); } else if (0 == strcmp(argv[1], "average")){ average(argc, argv); } else if (0 == strcmp(argv[1], "visualize")){ visualize(argv[2], (argc > 3) ? argv[3] : 0); + } else if (0 == strcmp(argv[1], "mkimg")){ + mkimg(argv[2], argv[3], atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), argv[7]); } else if (0 == strcmp(argv[1], "imtest")){ test_resize(argv[2]); } else { diff --git a/image.darknet/inst/include/darknet/examples/detector-scipy-opencv.py b/image.darknet/inst/include/darknet/examples/detector-scipy-opencv.py new file mode 100644 index 0000000..3bfc591 --- /dev/null +++ b/image.darknet/inst/include/darknet/examples/detector-scipy-opencv.py @@ -0,0 +1,56 @@ +# Stupid python path shit. +# Instead just add darknet.py to somewhere in your python path +# OK actually that might not be a great idea, idk, work in progress +# Use at your own risk. or don't, i don't care + +from scipy.misc import imread +import cv2 + +def array_to_image(arr): + arr = arr.transpose(2,0,1) + c = arr.shape[0] + h = arr.shape[1] + w = arr.shape[2] + arr = (arr/255.0).flatten() + data = dn.c_array(dn.c_float, arr) + im = dn.IMAGE(w,h,c,data) + return im + +def detect2(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45): + boxes = dn.make_boxes(net) + probs = dn.make_probs(net) + num = dn.num_boxes(net) + dn.network_detect(net, image, thresh, hier_thresh, nms, boxes, probs) + res = [] + for j in range(num): + for i in range(meta.classes): + if probs[j][i] > 0: + res.append((meta.names[i], probs[j][i], (boxes[j].x, boxes[j].y, boxes[j].w, boxes[j].h))) + res = sorted(res, key=lambda x: -x[1]) + dn.free_ptrs(dn.cast(probs, dn.POINTER(dn.c_void_p)), num) + return res + +import sys, os +sys.path.append(os.path.join(os.getcwd(),'python/')) + +import darknet as dn + +# Darknet +net = dn.load_net("cfg/tiny-yolo.cfg", "tiny-yolo.weights", 0) +meta = dn.load_meta("cfg/coco.data") +r = dn.detect(net, meta, "data/dog.jpg") +print r + +# scipy +arr= imread('data/dog.jpg') +im = array_to_image(arr) +r = detect2(net, meta, im) +print r + +# OpenCV +arr = cv2.imread('data/dog.jpg') +im = array_to_image(arr) +dn.rgbgr_image(im) +r = detect2(net, meta, im) +print r + diff --git a/image.darknet/inst/include/darknet/examples/detector.c b/image.darknet/inst/include/darknet/examples/detector.c new file mode 100644 index 0000000..318f7fb --- /dev/null +++ b/image.darknet/inst/include/darknet/examples/detector.c @@ -0,0 +1,850 @@ +#include "darknet.h" + +static int coco_ids[] = {1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,27,28,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,70,72,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,90}; + + +void train_detector(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear) +{ + list *options = read_data_cfg(datacfg); + char *train_images = option_find_str(options, "train", "data/train.list"); + char *backup_directory = option_find_str(options, "backup", "/backup/"); + + srand(time(0)); + char *base = basecfg(cfgfile); + printf("%s\n", base); + float avg_loss = -1; + network **nets = calloc(ngpus, sizeof(network)); + + srand(time(0)); + int seed = rand(); + int i; + for(i = 0; i < ngpus; ++i){ + srand(seed); +#ifdef GPU + cuda_set_device(gpus[i]); +#endif + nets[i] = load_network(cfgfile, weightfile, clear); + nets[i]->learning_rate *= ngpus; + } + srand(time(0)); + network *net = nets[0]; + + int imgs = net->batch * net->subdivisions * ngpus; + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + data train, buffer; + + layer l = net->layers[net->n - 1]; + + int classes = l.classes; + float jitter = l.jitter; + + list *plist = get_paths(train_images); + //int N = plist->size; + char **paths = (char **)list_to_array(plist); + + load_args args = get_base_args(net); + args.coords = l.coords; + args.paths = paths; + args.n = imgs; + args.m = plist->size; + args.classes = classes; + args.jitter = jitter; + args.num_boxes = l.max_boxes; + args.d = &buffer; + args.type = DETECTION_DATA; + //args.type = INSTANCE_DATA; + args.threads = 64; + + pthread_t load_thread = load_data(args); + double time; + int count = 0; + //while(i*imgs < N*120){ + while(get_current_batch(net) < net->max_batches){ + if(l.random && count++%10 == 0){ + printf("Resizing\n"); + int dim = (rand() % 10 + 10) * 32; + if (get_current_batch(net)+200 > net->max_batches) dim = 608; + //int dim = (rand() % 4 + 16) * 32; + printf("%d\n", dim); + args.w = dim; + args.h = dim; + + pthread_join(load_thread, 0); + train = buffer; + free_data(train); + load_thread = load_data(args); + + #pragma omp parallel for + for(i = 0; i < ngpus; ++i){ + resize_network(nets[i], dim, dim); + } + net = nets[0]; + } + time=what_time_is_it_now(); + pthread_join(load_thread, 0); + train = buffer; + load_thread = load_data(args); + + /* + int k; + for(k = 0; k < l.max_boxes; ++k){ + box b = float_to_box(train.y.vals[10] + 1 + k*5); + if(!b.x) break; + printf("loaded: %f %f %f %f\n", b.x, b.y, b.w, b.h); + } + */ + /* + int zz; + for(zz = 0; zz < train.X.cols; ++zz){ + image im = float_to_image(net->w, net->h, 3, train.X.vals[zz]); + int k; + for(k = 0; k < l.max_boxes; ++k){ + box b = float_to_box(train.y.vals[zz] + k*5, 1); + printf("%f %f %f %f\n", b.x, b.y, b.w, b.h); + draw_bbox(im, b, 1, 1,0,0); + } + show_image(im, "truth11"); + cvWaitKey(0); + save_image(im, "truth11"); + } + */ + + printf("Loaded: %lf seconds\n", what_time_is_it_now()-time); + + time=what_time_is_it_now(); + float loss = 0; +#ifdef GPU + if(ngpus == 1){ + loss = train_network(net, train); + } else { + loss = train_networks(nets, ngpus, train, 4); + } +#else + loss = train_network(net, train); +#endif + if (avg_loss < 0) avg_loss = loss; + avg_loss = avg_loss*.9 + loss*.1; + + i = get_current_batch(net); + printf("%ld: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), loss, avg_loss, get_current_rate(net), what_time_is_it_now()-time, i*imgs); + if(i%100==0){ +#ifdef GPU + if(ngpus != 1) sync_nets(nets, ngpus, 0); +#endif + char buff[256]; + sprintf(buff, "%s/%s.backup", backup_directory, base); + save_weights(net, buff); + } + if(i%10000==0 || (i < 1000 && i%100 == 0)){ +#ifdef GPU + if(ngpus != 1) sync_nets(nets, ngpus, 0); +#endif + char buff[256]; + sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); + save_weights(net, buff); + } + free_data(train); + } +#ifdef GPU + if(ngpus != 1) sync_nets(nets, ngpus, 0); +#endif + char buff[256]; + sprintf(buff, "%s/%s_final.weights", backup_directory, base); + save_weights(net, buff); +} + + +static int get_coco_image_id(char *filename) +{ + char *p = strrchr(filename, '/'); + char *c = strrchr(filename, '_'); + if(c) p = c; + return atoi(p+1); +} + +static void print_cocos(FILE *fp, char *image_path, detection *dets, int num_boxes, int classes, int w, int h) +{ + int i, j; + int image_id = get_coco_image_id(image_path); + for(i = 0; i < num_boxes; ++i){ + float xmin = dets[i].bbox.x - dets[i].bbox.w/2.; + float xmax = dets[i].bbox.x + dets[i].bbox.w/2.; + float ymin = dets[i].bbox.y - dets[i].bbox.h/2.; + float ymax = dets[i].bbox.y + dets[i].bbox.h/2.; + + if (xmin < 0) xmin = 0; + if (ymin < 0) ymin = 0; + if (xmax > w) xmax = w; + if (ymax > h) ymax = h; + + float bx = xmin; + float by = ymin; + float bw = xmax - xmin; + float bh = ymax - ymin; + + for(j = 0; j < classes; ++j){ + if (dets[i].prob[j]) fprintf(fp, "{\"image_id\":%d, \"category_id\":%d, \"bbox\":[%f, %f, %f, %f], \"score\":%f},\n", image_id, coco_ids[j], bx, by, bw, bh, dets[i].prob[j]); + } + } +} + +void print_detector_detections(FILE **fps, char *id, detection *dets, int total, int classes, int w, int h) +{ + int i, j; + for(i = 0; i < total; ++i){ + float xmin = dets[i].bbox.x - dets[i].bbox.w/2. + 1; + float xmax = dets[i].bbox.x + dets[i].bbox.w/2. + 1; + float ymin = dets[i].bbox.y - dets[i].bbox.h/2. + 1; + float ymax = dets[i].bbox.y + dets[i].bbox.h/2. + 1; + + if (xmin < 1) xmin = 1; + if (ymin < 1) ymin = 1; + if (xmax > w) xmax = w; + if (ymax > h) ymax = h; + + for(j = 0; j < classes; ++j){ + if (dets[i].prob[j]) fprintf(fps[j], "%s %f %f %f %f %f\n", id, dets[i].prob[j], + xmin, ymin, xmax, ymax); + } + } +} + +void print_imagenet_detections(FILE *fp, int id, detection *dets, int total, int classes, int w, int h) +{ + int i, j; + for(i = 0; i < total; ++i){ + float xmin = dets[i].bbox.x - dets[i].bbox.w/2.; + float xmax = dets[i].bbox.x + dets[i].bbox.w/2.; + float ymin = dets[i].bbox.y - dets[i].bbox.h/2.; + float ymax = dets[i].bbox.y + dets[i].bbox.h/2.; + + if (xmin < 0) xmin = 0; + if (ymin < 0) ymin = 0; + if (xmax > w) xmax = w; + if (ymax > h) ymax = h; + + for(j = 0; j < classes; ++j){ + int class = j; + if (dets[i].prob[class]) fprintf(fp, "%d %d %f %f %f %f %f\n", id, j+1, dets[i].prob[class], + xmin, ymin, xmax, ymax); + } + } +} + +void validate_detector_flip(char *datacfg, char *cfgfile, char *weightfile, char *outfile) +{ + int j; + list *options = read_data_cfg(datacfg); + char *valid_images = option_find_str(options, "valid", "data/train.list"); + char *name_list = option_find_str(options, "names", "data/names.list"); + char *prefix = option_find_str(options, "results", "results"); + char **names = get_labels(name_list); + char *mapf = option_find_str(options, "map", 0); + int *map = 0; + if (mapf) map = read_map(mapf); + + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 2); + fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + srand(time(0)); + + list *plist = get_paths(valid_images); + char **paths = (char **)list_to_array(plist); + + layer l = net->layers[net->n-1]; + int classes = l.classes; + + char buff[1024]; + char *type = option_find_str(options, "eval", "voc"); + FILE *fp = 0; + FILE **fps = 0; + int coco = 0; + int imagenet = 0; + if(0==strcmp(type, "coco")){ + if(!outfile) outfile = "coco_results"; + snprintf(buff, 1024, "%s/%s.json", prefix, outfile); + fp = fopen(buff, "w"); + fprintf(fp, "[\n"); + coco = 1; + } else if(0==strcmp(type, "imagenet")){ + if(!outfile) outfile = "imagenet-detection"; + snprintf(buff, 1024, "%s/%s.txt", prefix, outfile); + fp = fopen(buff, "w"); + imagenet = 1; + classes = 200; + } else { + if(!outfile) outfile = "comp4_det_test_"; + fps = calloc(classes, sizeof(FILE *)); + for(j = 0; j < classes; ++j){ + snprintf(buff, 1024, "%s/%s%s.txt", prefix, outfile, names[j]); + fps[j] = fopen(buff, "w"); + } + } + + int m = plist->size; + int i=0; + int t; + + float thresh = .005; + float nms = .45; + + int nthreads = 4; + image *val = calloc(nthreads, sizeof(image)); + image *val_resized = calloc(nthreads, sizeof(image)); + image *buf = calloc(nthreads, sizeof(image)); + image *buf_resized = calloc(nthreads, sizeof(image)); + pthread_t *thr = calloc(nthreads, sizeof(pthread_t)); + + image input = make_image(net->w, net->h, net->c*2); + + load_args args = {0}; + args.w = net->w; + args.h = net->h; + //args.type = IMAGE_DATA; + args.type = LETTERBOX_DATA; + + for(t = 0; t < nthreads; ++t){ + args.path = paths[i+t]; + args.im = &buf[t]; + args.resized = &buf_resized[t]; + thr[t] = load_data_in_thread(args); + } + double start = what_time_is_it_now(); + for(i = nthreads; i < m+nthreads; i += nthreads){ + fprintf(stderr, "%d\n", i); + for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ + pthread_join(thr[t], 0); + val[t] = buf[t]; + val_resized[t] = buf_resized[t]; + } + for(t = 0; t < nthreads && i+t < m; ++t){ + args.path = paths[i+t]; + args.im = &buf[t]; + args.resized = &buf_resized[t]; + thr[t] = load_data_in_thread(args); + } + for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ + char *path = paths[i+t-nthreads]; + char *id = basecfg(path); + copy_cpu(net->w*net->h*net->c, val_resized[t].data, 1, input.data, 1); + flip_image(val_resized[t]); + copy_cpu(net->w*net->h*net->c, val_resized[t].data, 1, input.data + net->w*net->h*net->c, 1); + + network_predict(net, input.data); + int w = val[t].w; + int h = val[t].h; + int num = 0; + detection *dets = get_network_boxes(net, w, h, thresh, .5, map, 0, &num); + if (nms) do_nms_sort(dets, num, classes, nms); + if (coco){ + print_cocos(fp, path, dets, num, classes, w, h); + } else if (imagenet){ + print_imagenet_detections(fp, i+t-nthreads+1, dets, num, classes, w, h); + } else { + print_detector_detections(fps, id, dets, num, classes, w, h); + } + free_detections(dets, num); + free(id); + free_image(val[t]); + free_image(val_resized[t]); + } + } + for(j = 0; j < classes; ++j){ + if(fps) fclose(fps[j]); + } + if(coco){ + fseek(fp, -2, SEEK_CUR); + fprintf(fp, "\n]\n"); + fclose(fp); + } + fprintf(stderr, "Total Detection Time: %f Seconds\n", what_time_is_it_now() - start); +} + + +void validate_detector(char *datacfg, char *cfgfile, char *weightfile, char *outfile) +{ + int j; + list *options = read_data_cfg(datacfg); + char *valid_images = option_find_str(options, "valid", "data/train.list"); + char *name_list = option_find_str(options, "names", "data/names.list"); + char *prefix = option_find_str(options, "results", "results"); + char **names = get_labels(name_list); + char *mapf = option_find_str(options, "map", 0); + int *map = 0; + if (mapf) map = read_map(mapf); + + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + srand(time(0)); + + list *plist = get_paths(valid_images); + char **paths = (char **)list_to_array(plist); + + layer l = net->layers[net->n-1]; + int classes = l.classes; + + char buff[1024]; + char *type = option_find_str(options, "eval", "voc"); + FILE *fp = 0; + FILE **fps = 0; + int coco = 0; + int imagenet = 0; + if(0==strcmp(type, "coco")){ + if(!outfile) outfile = "coco_results"; + snprintf(buff, 1024, "%s/%s.json", prefix, outfile); + fp = fopen(buff, "w"); + fprintf(fp, "[\n"); + coco = 1; + } else if(0==strcmp(type, "imagenet")){ + if(!outfile) outfile = "imagenet-detection"; + snprintf(buff, 1024, "%s/%s.txt", prefix, outfile); + fp = fopen(buff, "w"); + imagenet = 1; + classes = 200; + } else { + if(!outfile) outfile = "comp4_det_test_"; + fps = calloc(classes, sizeof(FILE *)); + for(j = 0; j < classes; ++j){ + snprintf(buff, 1024, "%s/%s%s.txt", prefix, outfile, names[j]); + fps[j] = fopen(buff, "w"); + } + } + + + int m = plist->size; + int i=0; + int t; + + float thresh = .005; + float nms = .45; + + int nthreads = 4; + image *val = calloc(nthreads, sizeof(image)); + image *val_resized = calloc(nthreads, sizeof(image)); + image *buf = calloc(nthreads, sizeof(image)); + image *buf_resized = calloc(nthreads, sizeof(image)); + pthread_t *thr = calloc(nthreads, sizeof(pthread_t)); + + load_args args = {0}; + args.w = net->w; + args.h = net->h; + //args.type = IMAGE_DATA; + args.type = LETTERBOX_DATA; + + for(t = 0; t < nthreads; ++t){ + args.path = paths[i+t]; + args.im = &buf[t]; + args.resized = &buf_resized[t]; + thr[t] = load_data_in_thread(args); + } + double start = what_time_is_it_now(); + for(i = nthreads; i < m+nthreads; i += nthreads){ + fprintf(stderr, "%d\n", i); + for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ + pthread_join(thr[t], 0); + val[t] = buf[t]; + val_resized[t] = buf_resized[t]; + } + for(t = 0; t < nthreads && i+t < m; ++t){ + args.path = paths[i+t]; + args.im = &buf[t]; + args.resized = &buf_resized[t]; + thr[t] = load_data_in_thread(args); + } + for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ + char *path = paths[i+t-nthreads]; + char *id = basecfg(path); + float *X = val_resized[t].data; + network_predict(net, X); + int w = val[t].w; + int h = val[t].h; + int nboxes = 0; + detection *dets = get_network_boxes(net, w, h, thresh, .5, map, 0, &nboxes); + if (nms) do_nms_sort(dets, nboxes, classes, nms); + if (coco){ + print_cocos(fp, path, dets, nboxes, classes, w, h); + } else if (imagenet){ + print_imagenet_detections(fp, i+t-nthreads+1, dets, nboxes, classes, w, h); + } else { + print_detector_detections(fps, id, dets, nboxes, classes, w, h); + } + free_detections(dets, nboxes); + free(id); + free_image(val[t]); + free_image(val_resized[t]); + } + } + for(j = 0; j < classes; ++j){ + if(fps) fclose(fps[j]); + } + if(coco){ + fseek(fp, -2, SEEK_CUR); + fprintf(fp, "\n]\n"); + fclose(fp); + } + fprintf(stderr, "Total Detection Time: %f Seconds\n", what_time_is_it_now() - start); +} + +void validate_detector_recall(char *cfgfile, char *weightfile) +{ + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + srand(time(0)); + + list *plist = get_paths("data/coco_val_5k.list"); + char **paths = (char **)list_to_array(plist); + + layer l = net->layers[net->n-1]; + + int j, k; + + int m = plist->size; + int i=0; + + float thresh = .001; + float iou_thresh = .5; + float nms = .4; + + int total = 0; + int correct = 0; + int proposals = 0; + float avg_iou = 0; + + for(i = 0; i < m; ++i){ + char *path = paths[i]; + image orig = load_image_color(path, 0, 0); + image sized = resize_image(orig, net->w, net->h); + char *id = basecfg(path); + network_predict(net, sized.data); + int nboxes = 0; + detection *dets = get_network_boxes(net, sized.w, sized.h, thresh, .5, 0, 1, &nboxes); + if (nms) do_nms_obj(dets, nboxes, 1, nms); + + char labelpath[4096]; + find_replace(path, "images", "labels", labelpath); + find_replace(labelpath, "JPEGImages", "labels", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + + int num_labels = 0; + box_label *truth = read_boxes(labelpath, &num_labels); + for(k = 0; k < nboxes; ++k){ + if(dets[k].objectness > thresh){ + ++proposals; + } + } + for (j = 0; j < num_labels; ++j) { + ++total; + box t = {truth[j].x, truth[j].y, truth[j].w, truth[j].h}; + float best_iou = 0; + for(k = 0; k < l.w*l.h*l.n; ++k){ + float iou = box_iou(dets[k].bbox, t); + if(dets[k].objectness > thresh && iou > best_iou){ + best_iou = iou; + } + } + avg_iou += best_iou; + if(best_iou > iou_thresh){ + ++correct; + } + } + + fprintf(stderr, "%5d %5d %5d\tRPs/Img: %.2f\tIOU: %.2f%%\tRecall:%.2f%%\n", i, correct, total, (float)proposals/(i+1), avg_iou*100/total, 100.*correct/total); + free(id); + free_image(orig); + free_image(sized); + } +} + + +void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char *outfile, int fullscreen) +{ + list *options = read_data_cfg(datacfg); + char *name_list = option_find_str(options, "names", "data/names.list"); + char **names = get_labels(name_list); + + image **alphabet = load_alphabet(); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + 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, "\n"); + } + image im = load_image_color(input,0,0); + 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.\n", input, what_time_is_it_now()-time); + int nboxes = 0; + detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes); + //printf("%d\n", 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); + free_detections(dets, nboxes); + if(outfile){ + save_image(im, 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; + } +} + +/* +void censor_detector(char *datacfg, char *cfgfile, char *weightfile, int cam_index, const char *filename, int class, float thresh, int skip) +{ +#ifdef OPENCV + char *base = basecfg(cfgfile); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + + srand(2222222); + CvCapture * cap; + + int w = 1280; + int h = 720; + + if(filename){ + cap = cvCaptureFromFile(filename); + }else{ + cap = cvCaptureFromCAM(cam_index); + } + + if(w){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FRAME_WIDTH, w); + } + if(h){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FRAME_HEIGHT, h); + } + + if(!cap) error("Couldn't connect to webcam.\n"); + cvNamedWindow(base, CV_WINDOW_NORMAL); + cvResizeWindow(base, 512, 512); + float fps = 0; + int i; + float nms = .45; + + while(1){ + image in = get_image_from_stream(cap); + //image in_s = resize_image(in, net->w, net->h); + image in_s = letterbox_image(in, net->w, net->h); + layer l = net->layers[net->n-1]; + + float *X = in_s.data; + network_predict(net, X); + int nboxes = 0; + detection *dets = get_network_boxes(net, in.w, in.h, thresh, 0, 0, 0, &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); + + for(i = 0; i < nboxes; ++i){ + if(dets[i].prob[class] > thresh){ + box b = dets[i].bbox; + int left = b.x-b.w/2.; + int top = b.y-b.h/2.; + censor_image(in, left, top, b.w, b.h); + } + } + show_image(in, base); + cvWaitKey(10); + free_detections(dets, nboxes); + + + free_image(in_s); + free_image(in); + + + float curr = 0; + fps = .9*fps + .1*curr; + for(i = 0; i < skip; ++i){ + image in = get_image_from_stream(cap); + free_image(in); + } + } + #endif +} + +void extract_detector(char *datacfg, char *cfgfile, char *weightfile, int cam_index, const char *filename, int class, float thresh, int skip) +{ +#ifdef OPENCV + char *base = basecfg(cfgfile); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + + srand(2222222); + CvCapture * cap; + + int w = 1280; + int h = 720; + + if(filename){ + cap = cvCaptureFromFile(filename); + }else{ + cap = cvCaptureFromCAM(cam_index); + } + + if(w){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FRAME_WIDTH, w); + } + if(h){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FRAME_HEIGHT, h); + } + + if(!cap) error("Couldn't connect to webcam.\n"); + cvNamedWindow(base, CV_WINDOW_NORMAL); + cvResizeWindow(base, 512, 512); + float fps = 0; + int i; + int count = 0; + float nms = .45; + + while(1){ + image in = get_image_from_stream(cap); + //image in_s = resize_image(in, net->w, net->h); + image in_s = letterbox_image(in, net->w, net->h); + layer l = net->layers[net->n-1]; + + show_image(in, base); + + int nboxes = 0; + float *X = in_s.data; + network_predict(net, X); + detection *dets = get_network_boxes(net, in.w, in.h, thresh, 0, 0, 1, &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); + + for(i = 0; i < nboxes; ++i){ + if(dets[i].prob[class] > thresh){ + box b = dets[i].bbox; + int size = b.w*in.w > b.h*in.h ? b.w*in.w : b.h*in.h; + int dx = b.x*in.w-size/2.; + int dy = b.y*in.h-size/2.; + image bim = crop_image(in, dx, dy, size, size); + char buff[2048]; + sprintf(buff, "results/extract/%07d", count); + ++count; + save_image(bim, buff); + free_image(bim); + } + } + free_detections(dets, nboxes); + + + free_image(in_s); + free_image(in); + + + float curr = 0; + fps = .9*fps + .1*curr; + for(i = 0; i < skip; ++i){ + image in = get_image_from_stream(cap); + free_image(in); + } + } + #endif +} +*/ + +/* +void network_detect(network *net, image im, float thresh, float hier_thresh, float nms, detection *dets) +{ + network_predict_image(net, im); + layer l = net->layers[net->n-1]; + int nboxes = num_boxes(net); + fill_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 0, dets); + if (nms) do_nms_sort(dets, nboxes, l.classes, nms); +} +*/ + +void run_detector(int argc, char **argv) +{ + char *prefix = find_char_arg(argc, argv, "-prefix", 0); + float thresh = find_float_arg(argc, argv, "-thresh", .5); + float hier_thresh = find_float_arg(argc, argv, "-hier", .5); + int cam_index = find_int_arg(argc, argv, "-c", 0); + int frame_skip = find_int_arg(argc, argv, "-s", 0); + int avg = find_int_arg(argc, argv, "-avg", 3); + if(argc < 4){ + fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); + return; + } + char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); + char *outfile = find_char_arg(argc, argv, "-out", 0); + int *gpus = 0; + int gpu = 0; + int ngpus = 0; + if(gpu_list){ + printf("%s\n", gpu_list); + int len = strlen(gpu_list); + ngpus = 1; + int i; + for(i = 0; i < len; ++i){ + if (gpu_list[i] == ',') ++ngpus; + } + gpus = calloc(ngpus, sizeof(int)); + for(i = 0; i < ngpus; ++i){ + gpus[i] = atoi(gpu_list); + gpu_list = strchr(gpu_list, ',')+1; + } + } else { + gpu = gpu_index; + gpus = &gpu; + ngpus = 1; + } + + int clear = find_arg(argc, argv, "-clear"); + int fullscreen = find_arg(argc, argv, "-fullscreen"); + int width = find_int_arg(argc, argv, "-w", 0); + int height = find_int_arg(argc, argv, "-h", 0); + int fps = find_int_arg(argc, argv, "-fps", 0); + //int class = find_int_arg(argc, argv, "-class", 0); + + char *datacfg = argv[3]; + char *cfg = argv[4]; + char *weights = (argc > 5) ? argv[5] : 0; + char *filename = (argc > 6) ? argv[6]: 0; + 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); + else if(0==strcmp(argv[2], "valid")) validate_detector(datacfg, cfg, weights, outfile); + else if(0==strcmp(argv[2], "valid2")) validate_detector_flip(datacfg, cfg, weights, outfile); + else if(0==strcmp(argv[2], "recall")) validate_detector_recall(cfg, weights); + else if(0==strcmp(argv[2], "demo")) { + list *options = read_data_cfg(datacfg); + int classes = option_find_int(options, "classes", 20); + char *name_list = option_find_str(options, "names", "data/names.list"); + char **names = get_labels(name_list); + demo(cfg, weights, thresh, cam_index, filename, names, classes, frame_skip, prefix, avg, hier_thresh, width, height, fps, fullscreen); + } + //else if(0==strcmp(argv[2], "extract")) extract_detector(datacfg, cfg, weights, cam_index, filename, class, thresh, frame_skip); + //else if(0==strcmp(argv[2], "censor")) censor_detector(datacfg, cfg, weights, cam_index, filename, class, thresh, frame_skip); +} diff --git a/image.darknet/inst/include/darknet/examples/detector.py b/image.darknet/inst/include/darknet/examples/detector.py new file mode 100644 index 0000000..40bb365 --- /dev/null +++ b/image.darknet/inst/include/darknet/examples/detector.py @@ -0,0 +1,27 @@ +# Stupid python path shit. +# Instead just add darknet.py to somewhere in your python path +# OK actually that might not be a great idea, idk, work in progress +# Use at your own risk. or don't, i don't care + +import sys, os +sys.path.append(os.path.join(os.getcwd(),'python/')) + +import darknet as dn +import pdb + +dn.set_gpu(0) +net = dn.load_net("cfg/yolo-thor.cfg", "/home/pjreddie/backup/yolo-thor_final.weights", 0) +meta = dn.load_meta("cfg/thor.data") +r = dn.detect(net, meta, "data/bedroom.jpg") +print r + +# And then down here you could detect a lot more images like: +r = dn.detect(net, meta, "data/eagle.jpg") +print r +r = dn.detect(net, meta, "data/giraffe.jpg") +print r +r = dn.detect(net, meta, "data/horses.jpg") +print r +r = dn.detect(net, meta, "data/person.jpg") +print r + diff --git a/image.darknet/inst/include/darknet/src/dice.c b/image.darknet/inst/include/darknet/examples/dice.c similarity index 95% rename from image.darknet/inst/include/darknet/src/dice.c rename to image.darknet/inst/include/darknet/examples/dice.c index 2286459..f56d76c 100644 --- a/image.darknet/inst/include/darknet/src/dice.c +++ b/image.darknet/inst/include/darknet/examples/dice.c @@ -1,6 +1,4 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" +#include "darknet.h" char *dice_labels[] = {"face1","face2","face3","face4","face5","face6"}; @@ -33,7 +31,7 @@ void train_dice(char *cfgfile, char *weightfile) float loss = train_network(net, train); if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - printf("%d: %f, %f avg, %lf seconds, %d images\n", i, loss, avg_loss, sec(clock()-time), *net.seen); + printf("%d: %f, %f avg, %lf seconds, %ld images\n", i, loss, avg_loss, sec(clock()-time), *net.seen); free_data(train); if((i % 100) == 0) net.learning_rate *= .1; if(i%100==0){ diff --git a/image.darknet/inst/include/darknet/examples/go.c b/image.darknet/inst/include/darknet/examples/go.c new file mode 100644 index 0000000..688579d --- /dev/null +++ b/image.darknet/inst/include/darknet/examples/go.c @@ -0,0 +1,1370 @@ +#include "darknet.h" + +#include +#include +#include + +int inverted = 1; +int noi = 1; +static const int nind = 10; +int legal_go(float *b, float *ko, int p, int r, int c); +int check_ko(float *x, float *ko); + +typedef struct { + char **data; + int n; +} moves; + +char *fgetgo(FILE *fp) +{ + if(feof(fp)) return 0; + size_t size = 96; + char *line = malloc(size*sizeof(char)); + if(size != fread(line, sizeof(char), size, fp)){ + free(line); + return 0; + } + + return line; +} + +moves load_go_moves(char *filename) +{ + moves m; + m.n = 128; + m.data = calloc(128, sizeof(char*)); + FILE *fp = fopen(filename, "rb"); + int count = 0; + char *line = 0; + while ((line = fgetgo(fp))) { + if (count >= m.n) { + m.n *= 2; + m.data = realloc(m.data, m.n*sizeof(char*)); + } + m.data[count] = line; + ++count; + } + printf("%d\n", count); + m.n = count; + m.data = realloc(m.data, count*sizeof(char*)); + return m; +} + +void string_to_board(char *s, float *board) +{ + int i, j; + memset(board, 0, 2*19*19*sizeof(float)); + int count = 0; + for(i = 0; i < 91; ++i){ + char c = s[i]; + for(j = 0; j < 4; ++j){ + int me = (c >> (2*j)) & 1; + int you = (c >> (2*j + 1)) & 1; + if (me) board[count] = 1; + else if (you) board[count + 19*19] = 1; + ++count; + if(count >= 19*19) break; + } + } +} + +void board_to_string(char *s, float *board) +{ + int i, j; + memset(s, 0, (19*19/4+1)*sizeof(char)); + int count = 0; + for(i = 0; i < 91; ++i){ + for(j = 0; j < 4; ++j){ + int me = (board[count] == 1); + int you = (board[count + 19*19] == 1); + if (me) s[i] = s[i] | (1<<(2*j)); + if (you) s[i] = s[i] | (1<<(2*j + 1)); + ++count; + if(count >= 19*19) break; + } + } +} + +static int occupied(float *b, int i) +{ + if (b[i]) return 1; + if (b[i+19*19]) return -1; + return 0; +} + +data random_go_moves(moves m, int n) +{ + data d = {0}; + d.X = make_matrix(n, 19*19*3); + d.y = make_matrix(n, 19*19+2); + int i, j; + for(i = 0; i < n; ++i){ + float *board = d.X.vals[i]; + float *label = d.y.vals[i]; + char *b = m.data[rand()%m.n]; + int player = b[0] - '0'; + int result = b[1] - '0'; + int row = b[2]; + int col = b[3]; + string_to_board(b+4, board); + if(player > 0) for(j = 0; j < 19*19; ++j) board[19*19*2 + j] = 1; + label[19*19+1] = (player==result); + if(row >= 19 || col >= 19){ + label[19*19] = 1; + } else { + label[col + 19*row] = 1; + if(occupied(board, col + 19*row)) printf("hey\n"); + } + + int flip = rand()%2; + int rotate = rand()%4; + image in = float_to_image(19, 19, 3, board); + image out = float_to_image(19, 19, 1, label); + if(flip){ + flip_image(in); + flip_image(out); + } + rotate_image_cw(in, rotate); + rotate_image_cw(out, rotate); + } + return d; +} + + +void train_go(char *cfgfile, char *weightfile, char *filename, int *gpus, int ngpus, int clear) +{ + int i; + float avg_loss = -1; + char *base = basecfg(cfgfile); + printf("%s\n", base); + printf("%d\n", ngpus); + network **nets = calloc(ngpus, sizeof(network*)); + + srand(time(0)); + int seed = rand(); + for(i = 0; i < ngpus; ++i){ + srand(seed); +#ifdef GPU + cuda_set_device(gpus[i]); +#endif + nets[i] = load_network(cfgfile, weightfile, clear); + nets[i]->learning_rate *= ngpus; + } + network *net = nets[0]; + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + + char *backup_directory = "/home/pjreddie/backup/"; + + char buff[256]; + moves m = load_go_moves(filename); + //moves m = load_go_moves("games.txt"); + + int N = m.n; + printf("Moves: %d\n", N); + int epoch = (*net->seen)/N; + while(get_current_batch(net) < net->max_batches || net->max_batches == 0){ + double time=what_time_is_it_now(); + + data train = random_go_moves(m, net->batch*net->subdivisions*ngpus); + printf("Loaded: %lf seconds\n", what_time_is_it_now() - time); + time=what_time_is_it_now(); + + float loss = 0; +#ifdef GPU + if(ngpus == 1){ + loss = train_network(net, train); + } else { + loss = train_networks(nets, ngpus, train, 10); + } +#else + loss = train_network(net, train); +#endif + free_data(train); + + if(avg_loss == -1) avg_loss = loss; + avg_loss = avg_loss*.95 + loss*.05; + printf("%ld, %.3f: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net->seen)/N, loss, avg_loss, get_current_rate(net), what_time_is_it_now()-time, *net->seen); + if(*net->seen/N > epoch){ + epoch = *net->seen/N; + char buff[256]; + sprintf(buff, "%s/%s_%d.weights", backup_directory,base, epoch); + save_weights(net, buff); + + } + if(get_current_batch(net)%1000 == 0){ + char buff[256]; + sprintf(buff, "%s/%s.backup",backup_directory,base); + save_weights(net, buff); + } + if(get_current_batch(net)%10000 == 0){ + char buff[256]; + sprintf(buff, "%s/%s_%ld.backup",backup_directory,base,get_current_batch(net)); + save_weights(net, buff); + } + } + sprintf(buff, "%s/%s.weights", backup_directory, base); + save_weights(net, buff); + + free_network(net); + free(base); +} + +static void propagate_liberty(float *board, int *lib, int *visited, int row, int col, int side) +{ + if (row < 0 || row > 18 || col < 0 || col > 18) return; + int index = row*19 + col; + if (occupied(board,index) != side) return; + if (visited[index]) return; + visited[index] = 1; + lib[index] += 1; + propagate_liberty(board, lib, visited, row+1, col, side); + propagate_liberty(board, lib, visited, row-1, col, side); + propagate_liberty(board, lib, visited, row, col+1, side); + propagate_liberty(board, lib, visited, row, col-1, side); +} + + +static int *calculate_liberties(float *board) +{ + int *lib = calloc(19*19, sizeof(int)); + int visited[19*19]; + int i, j; + for(j = 0; j < 19; ++j){ + for(i = 0; i < 19; ++i){ + memset(visited, 0, 19*19*sizeof(int)); + int index = j*19 + i; + if(!occupied(board,index)){ + if ((i > 0) && occupied(board,index - 1)) propagate_liberty(board, lib, visited, j, i-1, occupied(board,index-1)); + if ((i < 18) && occupied(board,index + 1)) propagate_liberty(board, lib, visited, j, i+1, occupied(board,index+1)); + if ((j > 0) && occupied(board,index - 19)) propagate_liberty(board, lib, visited, j-1, i, occupied(board,index-19)); + if ((j < 18) && occupied(board,index + 19)) propagate_liberty(board, lib, visited, j+1, i, occupied(board,index+19)); + } + } + } + return lib; +} + +void print_board(FILE *stream, float *board, int player, int *indexes) +{ + int i,j,n; + fprintf(stream, " "); + for(i = 0; i < 19; ++i){ + fprintf(stream, "%c ", 'A' + i + 1*(i > 7 && noi)); + } + fprintf(stream, "\n"); + for(j = 0; j < 19; ++j){ + fprintf(stream, "%2d", (inverted) ? 19-j : j+1); + for(i = 0; i < 19; ++i){ + int index = j*19 + i; + if(indexes){ + int found = 0; + for(n = 0; n < nind; ++n){ + if(index == indexes[n]){ + found = 1; + /* + if(n == 0) fprintf(stream, "\uff11"); + else if(n == 1) fprintf(stream, "\uff12"); + else if(n == 2) fprintf(stream, "\uff13"); + else if(n == 3) fprintf(stream, "\uff14"); + else if(n == 4) fprintf(stream, "\uff15"); + */ + fprintf(stream, " %d", n+1); + } + } + if(found) continue; + } + //if(board[index]*-swap > 0) fprintf(stream, "\u25C9 "); + //else if(board[index]*-swap < 0) fprintf(stream, "\u25EF "); + if (occupied(board, index) == player) fprintf(stream, " X"); + else if (occupied(board, index) ==-player) fprintf(stream, " O"); + else fprintf(stream, " ."); + } + fprintf(stream, "\n"); + } +} + +void flip_board(float *board) +{ + int i; + for(i = 0; i < 19*19; ++i){ + float swap = board[i]; + board[i] = board[i+19*19]; + board[i+19*19] = swap; + board[i+19*19*2] = 1-board[i+19*19*2]; + } +} + +float predict_move2(network *net, float *board, float *move, int multi) +{ + float *output = network_predict(net, board); + copy_cpu(19*19+1, output, 1, move, 1); + float result = output[19*19 + 1]; + int i; + if(multi){ + image bim = float_to_image(19, 19, 3, board); + for(i = 1; i < 8; ++i){ + rotate_image_cw(bim, i); + if(i >= 4) flip_image(bim); + + float *output = network_predict(net, board); + image oim = float_to_image(19, 19, 1, output); + result += output[19*19 + 1]; + + if(i >= 4) flip_image(oim); + rotate_image_cw(oim, -i); + + axpy_cpu(19*19+1, 1, output, 1, move, 1); + + if(i >= 4) flip_image(bim); + rotate_image_cw(bim, -i); + } + result = result/8; + scal_cpu(19*19+1, 1./8., move, 1); + } + for(i = 0; i < 19*19; ++i){ + if(board[i] || board[i+19*19]) move[i] = 0; + } + return result; +} + +static void remove_connected(float *b, int *lib, int p, int r, int c) +{ + if (r < 0 || r >= 19 || c < 0 || c >= 19) return; + if (occupied(b, r*19 + c) != p) return; + if (lib[r*19 + c] != 1) return; + b[r*19 + c] = 0; + b[19*19 + r*19 + c] = 0; + remove_connected(b, lib, p, r+1, c); + remove_connected(b, lib, p, r-1, c); + remove_connected(b, lib, p, r, c+1); + remove_connected(b, lib, p, r, c-1); +} + + +void move_go(float *b, int p, int r, int c) +{ + int *l = calculate_liberties(b); + if(p > 0) b[r*19 + c] = 1; + else b[19*19 + r*19 + c] = 1; + remove_connected(b, l, -p, r+1, c); + remove_connected(b, l, -p, r-1, c); + remove_connected(b, l, -p, r, c+1); + remove_connected(b, l, -p, r, c-1); + free(l); +} + +int compare_board(float *a, float *b) +{ + if(memcmp(a, b, 19*19*3*sizeof(float)) == 0) return 1; + return 0; +} + +typedef struct mcts_tree{ + float *board; + struct mcts_tree **children; + float *prior; + int *visit_count; + float *value; + float *mean; + float *prob; + int total_count; + float result; + int done; + int pass; +} mcts_tree; + +void free_mcts(mcts_tree *root) +{ + if(!root) return; + int i; + free(root->board); + for(i = 0; i < 19*19+1; ++i){ + if(root->children[i]) free_mcts(root->children[i]); + } + free(root->children); + free(root->prior); + free(root->visit_count); + free(root->value); + free(root->mean); + free(root->prob); + free(root); +} + +float *network_predict_rotations(network *net, float *next) +{ + int n = net->batch; + float *in = calloc(19*19*3*n, sizeof(float)); + image im = float_to_image(19, 19, 3, next); + int i,j; + int *inds = random_index_order(0, 8); + for(j = 0; j < n; ++j){ + i = inds[j]; + rotate_image_cw(im, i); + if(i >= 4) flip_image(im); + memcpy(in + 19*19*3*j, im.data, 19*19*3*sizeof(float)); + if(i >= 4) flip_image(im); + rotate_image_cw(im, -i); + } + float *pred = network_predict(net, in); + for(j = 0; j < n; ++j){ + i = inds[j]; + image im = float_to_image(19, 19, 1, pred + j*(19*19 + 2)); + if(i >= 4) flip_image(im); + rotate_image_cw(im, -i); + if(j > 0){ + axpy_cpu(19*19+2, 1, im.data, 1, pred, 1); + } + } + free(in); + free(inds); + scal_cpu(19*19+2, 1./n, pred, 1); + return pred; +} + +mcts_tree *expand(float *next, float *ko, network *net) +{ + mcts_tree *root = calloc(1, sizeof(mcts_tree)); + root->board = next; + root->children = calloc(19*19+1, sizeof(mcts_tree*)); + root->prior = calloc(19*19 + 1, sizeof(float)); + root->prob = calloc(19*19 + 1, sizeof(float)); + root->mean = calloc(19*19 + 1, sizeof(float)); + root->value = calloc(19*19 + 1, sizeof(float)); + root->visit_count = calloc(19*19 + 1, sizeof(int)); + root->total_count = 1; + int i; + float *pred = network_predict_rotations(net, next); + copy_cpu(19*19+1, pred, 1, root->prior, 1); + float val = 2*pred[19*19 + 1] - 1; + root->result = val; + for(i = 0; i < 19*19+1; ++i) { + root->visit_count[i] = 0; + root->value[i] = 0; + root->mean[i] = val; + if(i < 19*19 && occupied(next, i)){ + root->value[i] = -1; + root->mean[i] = -1; + root->prior[i] = 0; + } + } + //print_board(stderr, next, flip?-1:1, 0); + return root; +} + +float *copy_board(float *board) +{ + float *next = calloc(19*19*3, sizeof(float)); + copy_cpu(19*19*3, board, 1, next, 1); + return next; +} + +float select_mcts(mcts_tree *root, network *net, float *prev, float cpuct) +{ + if(root->done) return -root->result; + int i; + float max = -1000; + int max_i = 0; + for(i = 0; i < 19*19+1; ++i){ + root->prob[i] = root->mean[i] + cpuct*root->prior[i] * sqrt(root->total_count) / (1. + root->visit_count[i]); + if(root->prob[i] > max){ + max = root->prob[i]; + max_i = i; + } + } + float val; + i = max_i; + root->visit_count[i]++; + root->total_count++; + if (root->children[i]) { + val = select_mcts(root->children[i], net, root->board, cpuct); + } else { + if(max_i < 19*19 && !legal_go(root->board, prev, 1, max_i/19, max_i%19)) { + root->mean[i] = -1; + root->value[i] = -1; + root->prior[i] = 0; + --root->total_count; + return select_mcts(root, net, prev, cpuct); + //printf("Detected ko\n"); + //getchar(); + } else { + float *next = copy_board(root->board); + if (max_i < 19*19) { + move_go(next, 1, max_i / 19, max_i % 19); + } + flip_board(next); + root->children[i] = expand(next, root->board, net); + val = -root->children[i]->result; + if(max_i == 19*19){ + root->children[i]->pass = 1; + if (root->pass){ + root->children[i]->done = 1; + } + } + } + } + root->value[i] += val; + root->mean[i] = root->value[i]/root->visit_count[i]; + return -val; +} + +mcts_tree *run_mcts(mcts_tree *tree, network *net, float *board, float *ko, int player, int n, float cpuct, float secs) +{ + int i; + double t = what_time_is_it_now(); + if(player < 0) flip_board(board); + if(!tree) tree = expand(copy_board(board), ko, net); + assert(compare_board(tree->board, board)); + for(i = 0; i < n; ++i){ + if (secs > 0 && (what_time_is_it_now() - t) > secs) break; + int max_i = max_int_index(tree->visit_count, 19*19+1); + if (tree->visit_count[max_i] >= n) break; + select_mcts(tree, net, ko, cpuct); + } + if(player < 0) flip_board(board); + //fprintf(stderr, "%f Seconds\n", what_time_is_it_now() - t); + return tree; +} + +mcts_tree *move_mcts(mcts_tree *tree, int index) +{ + if(index < 0 || index > 19*19 || !tree || !tree->children[index]) { + free_mcts(tree); + tree = 0; + } else { + mcts_tree *swap = tree; + tree = tree->children[index]; + swap->children[index] = 0; + free_mcts(swap); + } + return tree; +} + +typedef struct { + float value; + float mcts; + int row; + int col; +} move; + +move pick_move(mcts_tree *tree, float temp, int player) +{ + int i; + float probs[19*19+1] = {0}; + move m = {0}; + double sum = 0; + /* + for(i = 0; i < 19*19+1; ++i){ + probs[i] = tree->visit_count[i]; + } + */ + //softmax(probs, 19*19+1, temp, 1, probs); + for(i = 0; i < 19*19+1; ++i){ + sum += pow(tree->visit_count[i], 1./temp); + } + for(i = 0; i < 19*19+1; ++i){ + probs[i] = pow(tree->visit_count[i], 1./temp) / sum; + } + + int index = sample_array(probs, 19*19+1); + m.row = index / 19; + m.col = index % 19; + m.value = (tree->result+1.)/2.; + m.mcts = (tree->mean[index]+1.)/2.; + + int indexes[nind]; + top_k(probs, 19*19+1, nind, indexes); + print_board(stderr, tree->board, player, indexes); + + fprintf(stderr, "%d %d, Result: %f, Prior: %f, Prob: %f, Mean Value: %f, Child Result: %f, Visited: %d\n", index/19, index%19, tree->result, tree->prior[index], probs[index], tree->mean[index], (tree->children[index])?tree->children[index]->result:0, tree->visit_count[index]); + int ind = max_index(probs, 19*19+1); + fprintf(stderr, "%d %d, Result: %f, Prior: %f, Prob: %f, Mean Value: %f, Child Result: %f, Visited: %d\n", ind/19, ind%19, tree->result, tree->prior[ind], probs[ind], tree->mean[ind], (tree->children[ind])?tree->children[ind]->result:0, tree->visit_count[ind]); + ind = max_index(tree->prior, 19*19+1); + fprintf(stderr, "%d %d, Result: %f, Prior: %f, Prob: %f, Mean Value: %f, Child Result: %f, Visited: %d\n", ind/19, ind%19, tree->result, tree->prior[ind], probs[ind], tree->mean[ind], (tree->children[ind])?tree->children[ind]->result:0, tree->visit_count[ind]); + return m; +} + +/* + float predict_move(network *net, float *board, float *move, int multi, float *ko, float temp) + { + + int i; + + int max_v = 0; + int max_i = 0; + for(i = 0; i < 19*19+1; ++i){ + if(root->visit_count[i] > max_v){ + max_v = root->visit_count[i]; + max_i = i; + } + } + fprintf(stderr, "%f Seconds\n", what_time_is_it_now() - t); + int ind = max_index(root->mean, 19*19+1); + fprintf(stderr, "%d %d, Result: %f, Prior: %f, Prob: %f, Mean Value: %f, Child Result: %f, Visited: %d\n", max_i/19, max_i%19, root->result, root->prior[max_i], root->prob[max_i], root->mean[max_i], (root->children[max_i])?root->children[max_i]->result:0, root->visit_count[max_i]); + fprintf(stderr, "%d %d, Result: %f, Prior: %f, Prob: %f, Mean Value: %f, Child Result: %f, Visited: %d\n", ind/19, ind%19, root->result, root->prior[ind], root->prob[ind], root->mean[ind], (root->children[ind])?root->children[ind]->result:0, root->visit_count[ind]); + ind = max_index(root->prior, 19*19+1); + fprintf(stderr, "%d %d, Result: %f, Prior: %f, Prob: %f, Mean Value: %f, Child Result: %f, Visited: %d\n", ind/19, ind%19, root->result, root->prior[ind], root->prob[ind], root->mean[ind], (root->children[ind])?root->children[ind]->result:0, root->visit_count[ind]); + if(root->result < -.9 && root->mean[max_i] < -.9) return -1000.f; + + float val = root->result; + free_mcts(root); + return val; + } + */ + +static int makes_safe_go(float *b, int *lib, int p, int r, int c){ + if (r < 0 || r >= 19 || c < 0 || c >= 19) return 0; + if (occupied(b,r*19 + c) == -p){ + if (lib[r*19 + c] > 1) return 0; + else return 1; + } + if (!occupied(b,r*19 + c)) return 1; + if (lib[r*19 + c] > 1) return 1; + return 0; +} + +int suicide_go(float *b, int p, int r, int c) +{ + int *l = calculate_liberties(b); + int safe = 0; + safe = safe || makes_safe_go(b, l, p, r+1, c); + safe = safe || makes_safe_go(b, l, p, r-1, c); + safe = safe || makes_safe_go(b, l, p, r, c+1); + safe = safe || makes_safe_go(b, l, p, r, c-1); + free(l); + return !safe; +} + +int check_ko(float *x, float *ko) +{ + if(!ko) return 0; + float curr[19*19*3]; + copy_cpu(19*19*3, x, 1, curr, 1); + if(curr[19*19*2] != ko[19*19*2]) flip_board(curr); + if(compare_board(curr, ko)) return 1; + return 0; +} + +int legal_go(float *b, float *ko, int p, int r, int c) +{ + if (occupied(b, r*19+c)) return 0; + float curr[19*19*3]; + copy_cpu(19*19*3, b, 1, curr, 1); + move_go(curr, p, r, c); + if(check_ko(curr, ko)) return 0; + if(suicide_go(b, p, r, c)) return 0; + return 1; +} + +/* + move generate_move(mcts_tree *root, network *net, int player, float *board, int multi, float temp, float *ko, int print) + { + move m = {0}; +//root = run_mcts(tree, network *net, float *board, float *ko, int n, float cpuct) +int i, j; +int empty = 1; +for(i = 0; i < 19*19; ++i){ +if (occupied(board, i)) { +empty = 0; +break; +} +} +if(empty) { +m.value = .5; +m.mcts = .5; +m.row = 3; +m.col = 15; +return m; +} + +float move[362]; +if (player < 0) flip_board(board); +float result = predict_move(net, board, move, multi, ko, temp); +if (player < 0) flip_board(board); +if(result == -1000.f) return -2; + +for(i = 0; i < 19; ++i){ +for(j = 0; j < 19; ++j){ +if (!legal_go(board, ko, player, i, j)) move[i*19 + j] = 0; +} +} + +int indexes[nind]; +top_k(move, 19*19+1, nind, indexes); + + +int max = max_index(move, 19*19+1); +int row = max / 19; +int col = max % 19; +int index = sample_array(move, 19*19+1); + +if(print){ +top_k(move, 19*19+1, nind, indexes); +for(i = 0; i < nind; ++i){ +if (!move[indexes[i]]) indexes[i] = -1; +} +print_board(stderr, board, 1, indexes); +fprintf(stderr, "%s To Move\n", player > 0 ? "X" : "O"); +fprintf(stderr, "%.2f%% Win Chance\n", (result+1)/2*100); +for(i = 0; i < nind; ++i){ +int index = indexes[i]; +int row = index / 19; +int col = index % 19; +if(row == 19){ +fprintf(stderr, "%d: Pass, %.2f%%\n", i+1, move[index]*100); +} else { +fprintf(stderr, "%d: %c %d, %.2f%%\n", i+1, col + 'A' + 1*(col > 7 && noi), (inverted)?19 - row : row+1, move[index]*100); +} +} +} +if (row == 19) return -1; + +if (suicide_go(board, player, row, col)){ +return -1; +} + +if (suicide_go(board, player, index/19, index%19)){ +index = max; +} +if (index == 19*19) return -1; +return index; +} +*/ + +void valid_go(char *cfgfile, char *weightfile, int multi, char *filename) +{ + srand(time(0)); + char *base = basecfg(cfgfile); + printf("%s\n", base); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + + float *board = calloc(19*19*3, sizeof(float)); + float *move = calloc(19*19+2, sizeof(float)); + // moves m = load_go_moves("/home/pjreddie/backup/go.test"); + moves m = load_go_moves(filename); + + int N = m.n; + int i,j; + int correct = 0; + for (i = 0; i 0) for(j = 0; j < 19*19; ++j) board[19*19*2 + j] = 1; + predict_move2(net, board, move, multi); + int index = max_index(move, 19*19+1); + if(index == truth) ++correct; + printf("%d Accuracy %f\n", i, (float) correct/(i+1)); + } +} + +int print_game(float *board, FILE *fp) +{ + int i, j; + int count = 3; + fprintf(fp, "komi 6.5\n"); + fprintf(fp, "boardsize 19\n"); + fprintf(fp, "clear_board\n"); + for(j = 0; j < 19; ++j){ + for(i = 0; i < 19; ++i){ + if(occupied(board,j*19 + i) == 1) fprintf(fp, "play black %c%d\n", 'A'+i+(i>=8), 19-j); + if(occupied(board,j*19 + i) == -1) fprintf(fp, "play white %c%d\n", 'A'+i+(i>=8), 19-j); + if(occupied(board,j*19 + i)) ++count; + } + } + return count; +} + + +int stdin_ready() +{ + fd_set readfds; + FD_ZERO(&readfds); + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_SET(STDIN_FILENO, &readfds); + + if (select(1, &readfds, NULL, NULL, &timeout)){ + return 1; + } + return 0; +} + +mcts_tree *ponder(mcts_tree *tree, network *net, float *b, float *ko, int player, float cpuct) +{ + double t = what_time_is_it_now(); + int count = 0; + if (tree) count = tree->total_count; + while(!stdin_ready()){ + if (what_time_is_it_now() - t > 120) break; + tree = run_mcts(tree, net, b, ko, player, 100000, cpuct, .1); + } + fprintf(stderr, "Pondered %d moves...\n", tree->total_count - count); + return tree; +} + +void engine_go(char *filename, char *weightfile, int mcts_iters, float secs, float temp, float cpuct, int anon, int resign) +{ + mcts_tree *root = 0; + network *net = load_network(filename, weightfile, 0); + set_batch_network(net, 1); + srand(time(0)); + float *board = calloc(19*19*3, sizeof(float)); + flip_board(board); + float *one = calloc(19*19*3, sizeof(float)); + float *two = calloc(19*19*3, sizeof(float)); + int ponder_player = 0; + int passed = 0; + int move_num = 0; + int main_time = 0; + int byo_yomi_time = 0; + int byo_yomi_stones = 0; + int black_time_left = 0; + int black_stones_left = 0; + int white_time_left = 0; + int white_stones_left = 0; + float orig_time = secs; + int old_ponder = 0; + while(1){ + if(ponder_player){ + root = ponder(root, net, board, two, ponder_player, cpuct); + } + old_ponder = ponder_player; + ponder_player = 0; + char buff[256]; + int id = 0; + int has_id = (scanf("%d", &id) == 1); + scanf("%s", buff); + if (feof(stdin)) break; + fprintf(stderr, "%s\n", buff); + char ids[256]; + sprintf(ids, "%d", id); + //fprintf(stderr, "%s\n", buff); + if (!has_id) ids[0] = 0; + if (!strcmp(buff, "protocol_version")){ + printf("=%s 2\n\n", ids); + } else if (!strcmp(buff, "name")){ + if(anon){ + printf("=%s The Fool!\n\n", ids); + }else{ + printf("=%s DarkGo\n\n", ids); + } + } else if (!strcmp(buff, "time_settings")){ + ponder_player = old_ponder; + scanf("%d %d %d", &main_time, &byo_yomi_time, &byo_yomi_stones); + printf("=%s \n\n", ids); + } else if (!strcmp(buff, "time_left")){ + ponder_player = old_ponder; + char color[256]; + int time = 0, stones = 0; + scanf("%s %d %d", color, &time, &stones); + if (color[0] == 'b' || color[0] == 'B'){ + black_time_left = time; + black_stones_left = stones; + } else { + white_time_left = time; + white_stones_left = stones; + } + printf("=%s \n\n", ids); + } else if (!strcmp(buff, "version")){ + if(anon){ + printf("=%s :-DDDD\n\n", ids); + }else { + printf("=%s 1.0. Want more DarkGo? You can find me on OGS, unlimited games, no waiting! https://online-go.com/user/view/434218\n\n", ids); + } + } else if (!strcmp(buff, "known_command")){ + char comm[256]; + scanf("%s", comm); + int known = (!strcmp(comm, "protocol_version") || + !strcmp(comm, "name") || + !strcmp(comm, "version") || + !strcmp(comm, "known_command") || + !strcmp(comm, "list_commands") || + !strcmp(comm, "quit") || + !strcmp(comm, "boardsize") || + !strcmp(comm, "clear_board") || + !strcmp(comm, "komi") || + !strcmp(comm, "final_status_list") || + !strcmp(comm, "play") || + !strcmp(comm, "genmove_white") || + !strcmp(comm, "genmove_black") || + !strcmp(comm, "fixed_handicap") || + !strcmp(comm, "genmove")); + if(known) printf("=%s true\n\n", ids); + else printf("=%s false\n\n", ids); + } else if (!strcmp(buff, "list_commands")){ + printf("=%s protocol_version\nshowboard\nname\nversion\nknown_command\nlist_commands\nquit\nboardsize\nclear_board\nkomi\nplay\ngenmove_black\ngenmove_white\ngenmove\nfinal_status_list\nfixed_handicap\n\n", ids); + } else if (!strcmp(buff, "quit")){ + break; + } else if (!strcmp(buff, "boardsize")){ + int boardsize = 0; + scanf("%d", &boardsize); + //fprintf(stderr, "%d\n", boardsize); + if(boardsize != 19){ + printf("?%s unacceptable size\n\n", ids); + } else { + root = move_mcts(root, -1); + memset(board, 0, 3*19*19*sizeof(float)); + flip_board(board); + move_num = 0; + printf("=%s \n\n", ids); + } + } else if (!strcmp(buff, "fixed_handicap")){ + int handicap = 0; + scanf("%d", &handicap); + int indexes[] = {72, 288, 300, 60, 180, 174, 186, 66, 294}; + int i; + for(i = 0; i < handicap; ++i){ + board[indexes[i]] = 1; + ++move_num; + } + root = move_mcts(root, -1); + } else if (!strcmp(buff, "clear_board")){ + passed = 0; + memset(board, 0, 3*19*19*sizeof(float)); + flip_board(board); + move_num = 0; + root = move_mcts(root, -1); + printf("=%s \n\n", ids); + } else if (!strcmp(buff, "komi")){ + float komi = 0; + scanf("%f", &komi); + printf("=%s \n\n", ids); + } else if (!strcmp(buff, "showboard")){ + printf("=%s \n", ids); + print_board(stdout, board, 1, 0); + printf("\n"); + } else if (!strcmp(buff, "play") || !strcmp(buff, "black") || !strcmp(buff, "white")){ + ++move_num; + char color[256]; + if(!strcmp(buff, "play")) + { + scanf("%s ", color); + } else { + scanf(" "); + color[0] = buff[0]; + } + char c; + int r; + int count = scanf("%c%d", &c, &r); + int player = (color[0] == 'b' || color[0] == 'B') ? 1 : -1; + if((c == 'p' || c == 'P') && count < 2) { + passed = 1; + printf("=%s \n\n", ids); + char *line = fgetl(stdin); + free(line); + fflush(stdout); + fflush(stderr); + root = move_mcts(root, 19*19); + continue; + } else { + passed = 0; + } + if(c >= 'A' && c <= 'Z') c = c - 'A'; + if(c >= 'a' && c <= 'z') c = c - 'a'; + if(c >= 8) --c; + r = 19 - r; + fprintf(stderr, "move: %d %d\n", r, c); + + float *swap = two; + two = one; + one = swap; + move_go(board, player, r, c); + copy_cpu(19*19*3, board, 1, one, 1); + if(root) fprintf(stderr, "Prior: %f\n", root->prior[r*19 + c]); + if(root) fprintf(stderr, "Mean: %f\n", root->mean[r*19 + c]); + if(root) fprintf(stderr, "Result: %f\n", root->result); + root = move_mcts(root, r*19 + c); + if(root) fprintf(stderr, "Visited: %d\n", root->total_count); + else fprintf(stderr, "NOT VISITED\n"); + + printf("=%s \n\n", ids); + //print_board(stderr, board, 1, 0); + } else if (!strcmp(buff, "genmove") || !strcmp(buff, "genmove_black") || !strcmp(buff, "genmove_white")){ + ++move_num; + int player = 0; + if(!strcmp(buff, "genmove")){ + char color[256]; + scanf("%s", color); + player = (color[0] == 'b' || color[0] == 'B') ? 1 : -1; + } else if (!strcmp(buff, "genmove_black")){ + player = 1; + } else { + player = -1; + } + if(player > 0){ + if(black_time_left <= 30) secs = 2.5; + else secs = orig_time; + } else { + if(white_time_left <= 30) secs = 2.5; + else secs = orig_time; + } + ponder_player = -player; + + //tree = generate_move(net, player, board, multi, .1, two, 1); + double t = what_time_is_it_now(); + root = run_mcts(root, net, board, two, player, mcts_iters, cpuct, secs); + fprintf(stderr, "%f Seconds\n", what_time_is_it_now() - t); + move m = pick_move(root, temp, player); + root = move_mcts(root, m.row*19 + m.col); + + + if(move_num > resign && m.value < .1 && m.mcts < .1){ + printf("=%s resign\n\n", ids); + } else if(m.row == 19){ + printf("=%s pass\n\n", ids); + passed = 0; + } else { + int row = m.row; + int col = m.col; + + float *swap = two; + two = one; + one = swap; + + move_go(board, player, row, col); + copy_cpu(19*19*3, board, 1, one, 1); + row = 19 - row; + if (col >= 8) ++col; + printf("=%s %c%d\n\n", ids, 'A' + col, row); + } + + } else if (!strcmp(buff, "p")){ + //print_board(board, 1, 0); + } else if (!strcmp(buff, "final_status_list")){ + char type[256]; + scanf("%s", type); + fprintf(stderr, "final_status\n"); + char *line = fgetl(stdin); + free(line); + if(type[0] == 'd' || type[0] == 'D'){ + int i; + FILE *f = fopen("game.txt", "w"); + int count = print_game(board, f); + fprintf(f, "%s final_status_list dead\n", ids); + fclose(f); + FILE *p = popen("./gnugo --mode gtp < game.txt", "r"); + for(i = 0; i < count; ++i){ + free(fgetl(p)); + free(fgetl(p)); + } + char *l = 0; + while((l = fgetl(p))){ + printf("%s\n", l); + free(l); + } + } else { + printf("?%s unknown command\n\n", ids); + } + } else if (!strcmp(buff, "kgs-genmove_cleanup")){ + char type[256]; + scanf("%s", type); + fprintf(stderr, "kgs-genmove_cleanup\n"); + char *line = fgetl(stdin); + free(line); + int i; + FILE *f = fopen("game.txt", "w"); + int count = print_game(board, f); + fprintf(f, "%s kgs-genmove_cleanup %s\n", ids, type); + fclose(f); + FILE *p = popen("./gnugo --mode gtp < game.txt", "r"); + for(i = 0; i < count; ++i){ + free(fgetl(p)); + free(fgetl(p)); + } + char *l = 0; + while((l = fgetl(p))){ + printf("%s\n", l); + free(l); + } + } else { + char *line = fgetl(stdin); + free(line); + printf("?%s unknown command\n\n", ids); + } + fflush(stdout); + fflush(stderr); + } + printf("%d %d %d\n",passed, black_stones_left, white_stones_left); +} + +void test_go(char *cfg, char *weights, int multi) +{ + int i; + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); + srand(time(0)); + float *board = calloc(19*19*3, sizeof(float)); + flip_board(board); + float *move = calloc(19*19+1, sizeof(float)); + int color = 1; + while(1){ + float result = predict_move2(net, board, move, multi); + printf("%.2f%% Win Chance\n", (result+1)/2*100); + + int indexes[nind]; + int row, col; + top_k(move, 19*19+1, nind, indexes); + print_board(stderr, board, color, indexes); + for(i = 0; i < nind; ++i){ + int index = indexes[i]; + row = index / 19; + col = index % 19; + if(row == 19){ + printf("%d: Pass, %.2f%%\n", i+1, move[index]*100); + } else { + printf("%d: %c %d, %.2f%%\n", i+1, col + 'A' + 1*(col > 7 && noi), (inverted)?19 - row : row+1, move[index]*100); + } + } + //if(color == 1) printf("\u25EF Enter move: "); + //else printf("\u25C9 Enter move: "); + if(color == 1) printf("X Enter move: "); + else printf("O Enter move: "); + + char c; + char *line = fgetl(stdin); + int picked = 1; + int dnum = sscanf(line, "%d", &picked); + int cnum = sscanf(line, "%c", &c); + if (strlen(line) == 0 || dnum) { + --picked; + if (picked < nind){ + int index = indexes[picked]; + row = index / 19; + col = index % 19; + if(row < 19){ + move_go(board, 1, row, col); + } + } + } else if (cnum){ + if (c <= 'T' && c >= 'A'){ + int num = sscanf(line, "%c %d", &c, &row); + row = (inverted)?19 - row : row-1; + col = c - 'A'; + if (col > 7 && noi) col -= 1; + if (num == 2) move_go(board, 1, row, col); + } else if (c == 'p') { + // Pass + } else if(c=='b' || c == 'w'){ + char g; + int num = sscanf(line, "%c %c %d", &g, &c, &row); + row = (inverted)?19 - row : row-1; + col = c - 'A'; + if (col > 7 && noi) col -= 1; + if (num == 3) { + int mc = (g == 'b') ? 1 : -1; + if (mc == color) { + board[row*19 + col] = 1; + } else { + board[19*19 + row*19 + col] = 1; + } + } + } else if(c == 'c'){ + char g; + int num = sscanf(line, "%c %c %d", &g, &c, &row); + row = (inverted)?19 - row : row-1; + col = c - 'A'; + if (col > 7 && noi) col -= 1; + if (num == 3) { + board[row*19 + col] = 0; + board[19*19 + row*19 + col] = 0; + } + } + } + free(line); + flip_board(board); + color = -color; + } +} + +float score_game(float *board) +{ + int i; + FILE *f = fopen("game.txt", "w"); + int count = print_game(board, f); + fprintf(f, "final_score\n"); + fclose(f); + FILE *p = popen("./gnugo --mode gtp < game.txt", "r"); + for(i = 0; i < count; ++i){ + free(fgetl(p)); + free(fgetl(p)); + } + char *l = 0; + float score = 0; + char player = 0; + while((l = fgetl(p))){ + fprintf(stderr, "%s \t", l); + int n = sscanf(l, "= %c+%f", &player, &score); + free(l); + if (n == 2) break; + } + if(player == 'W') score = -score; + pclose(p); + return score; +} + +void self_go(char *filename, char *weightfile, char *f2, char *w2, int multi) +{ + mcts_tree *tree1 = 0; + mcts_tree *tree2 = 0; + network *net = load_network(filename, weightfile, 0); + //set_batch_network(net, 1); + + network *net2; + if (f2) { + net2 = parse_network_cfg(f2); + if(w2){ + load_weights(net2, w2); + } + } else { + net2 = calloc(1, sizeof(network)); + *net2 = *net; + } + srand(time(0)); + char boards[600][93]; + int count = 0; + //set_batch_network(net, 1); + //set_batch_network(net2, 1); + float *board = calloc(19*19*3, sizeof(float)); + flip_board(board); + float *one = calloc(19*19*3, sizeof(float)); + float *two = calloc(19*19*3, sizeof(float)); + int done = 0; + int player = 1; + int p1 = 0; + int p2 = 0; + int total = 0; + float temp = .1; + int mcts_iters = 500; + float cpuct = 5; + while(1){ + if (done){ + tree1 = move_mcts(tree1, -1); + tree2 = move_mcts(tree2, -1); + float score = score_game(board); + if((score > 0) == (total%2==0)) ++p1; + else ++p2; + ++total; + fprintf(stderr, "Total: %d, Player 1: %f, Player 2: %f\n", total, (float)p1/total, (float)p2/total); + sleep(1); + /* + int i = (score > 0)? 0 : 1; + int j; + for(; i < count; i += 2){ + for(j = 0; j < 93; ++j){ + printf("%c", boards[i][j]); + } + printf("\n"); + } + */ + memset(board, 0, 3*19*19*sizeof(float)); + flip_board(board); + player = 1; + done = 0; + count = 0; + fflush(stdout); + fflush(stderr); + } + //print_board(stderr, board, 1, 0); + //sleep(1); + + if ((total%2==0) == (player==1)){ + //mcts_iters = 4500; + cpuct = 5; + } else { + //mcts_iters = 500; + cpuct = 1; + } + network *use = ((total%2==0) == (player==1)) ? net : net2; + mcts_tree *t = ((total%2==0) == (player==1)) ? tree1 : tree2; + t = run_mcts(t, use, board, two, player, mcts_iters, cpuct, 0); + move m = pick_move(t, temp, player); + if(((total%2==0) == (player==1))) tree1 = t; + else tree2 = t; + + tree1 = move_mcts(tree1, m.row*19 + m.col); + tree2 = move_mcts(tree2, m.row*19 + m.col); + + if(m.row == 19){ + done = 1; + continue; + } + int row = m.row; + int col = m.col; + + float *swap = two; + two = one; + one = swap; + + if(player < 0) flip_board(board); + boards[count][0] = row; + boards[count][1] = col; + board_to_string(boards[count] + 2, board); + if(player < 0) flip_board(board); + ++count; + + move_go(board, player, row, col); + copy_cpu(19*19*3, board, 1, one, 1); + + player = -player; + } +} + +void run_go(int argc, char **argv) +{ + //boards_go(); + if(argc < 4){ + fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); + return; + } + + char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); + int *gpus = 0; + int gpu = 0; + int ngpus = 0; + if(gpu_list){ + printf("%s\n", gpu_list); + int len = strlen(gpu_list); + ngpus = 1; + int i; + for(i = 0; i < len; ++i){ + if (gpu_list[i] == ',') ++ngpus; + } + gpus = calloc(ngpus, sizeof(int)); + for(i = 0; i < ngpus; ++i){ + gpus[i] = atoi(gpu_list); + gpu_list = strchr(gpu_list, ',')+1; + } + } else { + gpu = gpu_index; + gpus = &gpu; + ngpus = 1; + } + int clear = find_arg(argc, argv, "-clear"); + + char *cfg = argv[3]; + char *weights = (argc > 4) ? argv[4] : 0; + char *c2 = (argc > 5) ? argv[5] : 0; + char *w2 = (argc > 6) ? argv[6] : 0; + int multi = find_arg(argc, argv, "-multi"); + int anon = find_arg(argc, argv, "-anon"); + int iters = find_int_arg(argc, argv, "-iters", 500); + int resign = find_int_arg(argc, argv, "-resign", 175); + float cpuct = find_float_arg(argc, argv, "-cpuct", 5); + float temp = find_float_arg(argc, argv, "-temp", .1); + float time = find_float_arg(argc, argv, "-time", 0); + if(0==strcmp(argv[2], "train")) train_go(cfg, weights, c2, gpus, ngpus, clear); + else if(0==strcmp(argv[2], "valid")) valid_go(cfg, weights, multi, c2); + else if(0==strcmp(argv[2], "self")) self_go(cfg, weights, c2, w2, multi); + else if(0==strcmp(argv[2], "test")) test_go(cfg, weights, multi); + else if(0==strcmp(argv[2], "engine")) engine_go(cfg, weights, iters, time, temp, cpuct, anon, resign); +} + + diff --git a/image.darknet/inst/include/darknet/examples/instance-segmenter.c b/image.darknet/inst/include/darknet/examples/instance-segmenter.c new file mode 100644 index 0000000..664e714 --- /dev/null +++ b/image.darknet/inst/include/darknet/examples/instance-segmenter.c @@ -0,0 +1,267 @@ +#include "darknet.h" +#include +#include + +void normalize_image2(image p); +void train_isegmenter(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear, int display) +{ + int i; + + float avg_loss = -1; + char *base = basecfg(cfgfile); + printf("%s\n", base); + printf("%d\n", ngpus); + network **nets = calloc(ngpus, sizeof(network*)); + + srand(time(0)); + int seed = rand(); + for(i = 0; i < ngpus; ++i){ + srand(seed); +#ifdef GPU + cuda_set_device(gpus[i]); +#endif + nets[i] = load_network(cfgfile, weightfile, clear); + nets[i]->learning_rate *= ngpus; + } + srand(time(0)); + network *net = nets[0]; + image pred = get_network_image(net); + + image embed = pred; + embed.c = 3; + embed.data += embed.w*embed.h*80; + + int div = net->w/pred.w; + assert(pred.w * div == net->w); + assert(pred.h * div == net->h); + + int imgs = net->batch * net->subdivisions * ngpus; + + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + list *options = read_data_cfg(datacfg); + + char *backup_directory = option_find_str(options, "backup", "/backup/"); + char *train_list = option_find_str(options, "train", "data/train.list"); + + list *plist = get_paths(train_list); + char **paths = (char **)list_to_array(plist); + printf("%d\n", plist->size); + int N = plist->size; + + load_args args = {0}; + args.w = net->w; + args.h = net->h; + args.threads = 32; + args.scale = div; + args.num_boxes = 90; + + args.min = net->min_crop; + args.max = net->max_crop; + args.angle = net->angle; + args.aspect = net->aspect; + args.exposure = net->exposure; + args.saturation = net->saturation; + args.hue = net->hue; + args.size = net->w; + args.classes = 80; + + args.paths = paths; + args.n = imgs; + args.m = N; + args.type = ISEG_DATA; + + data train; + data buffer; + pthread_t load_thread; + args.d = &buffer; + load_thread = load_data(args); + + int epoch = (*net->seen)/N; + while(get_current_batch(net) < net->max_batches || net->max_batches == 0){ + double time = what_time_is_it_now(); + + pthread_join(load_thread, 0); + train = buffer; + load_thread = load_data(args); + + printf("Loaded: %lf seconds\n", what_time_is_it_now()-time); + time = what_time_is_it_now(); + + float loss = 0; +#ifdef GPU + if(ngpus == 1){ + loss = train_network(net, train); + } else { + loss = train_networks(nets, ngpus, train, 4); + } +#else + loss = train_network(net, train); +#endif + if(display){ + image tr = float_to_image(net->w/div, net->h/div, 80, train.y.vals[net->batch*(net->subdivisions-1)]); + image im = float_to_image(net->w, net->h, net->c, train.X.vals[net->batch*(net->subdivisions-1)]); + pred.c = 80; + image mask = mask_to_rgb(tr); + image prmask = mask_to_rgb(pred); + image ecopy = copy_image(embed); + normalize_image2(ecopy); + show_image(ecopy, "embed", 1); + free_image(ecopy); + + show_image(im, "input", 1); + show_image(prmask, "pred", 1); + show_image(mask, "truth", 100); + free_image(mask); + free_image(prmask); + } + if(avg_loss == -1) avg_loss = loss; + avg_loss = avg_loss*.9 + loss*.1; + printf("%ld, %.3f: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net->seen)/N, loss, avg_loss, get_current_rate(net), what_time_is_it_now()-time, *net->seen); + free_data(train); + if(*net->seen/N > epoch){ + epoch = *net->seen/N; + char buff[256]; + sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); + save_weights(net, buff); + } + if(get_current_batch(net)%100 == 0){ + char buff[256]; + sprintf(buff, "%s/%s.backup",backup_directory,base); + save_weights(net, buff); + } + } + char buff[256]; + sprintf(buff, "%s/%s.weights", backup_directory, base); + save_weights(net, buff); + + free_network(net); + free_ptrs((void**)paths, plist->size); + free_list(plist); + free(base); +} + +void predict_isegmenter(char *datafile, char *cfg, char *weights, char *filename) +{ + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); + srand(2222222); + + clock_t time; + char buff[256]; + char *input = buff; + 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, "\n"); + } + image im = load_image_color(input, 0, 0); + image sized = letterbox_image(im, net->w, net->h); + + float *X = sized.data; + time=clock(); + float *predictions = network_predict(net, X); + image pred = get_network_image(net); + image prmask = mask_to_rgb(pred); + printf("Predicted: %f\n", predictions[0]); + printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); + show_image(sized, "orig", 1); + show_image(prmask, "pred", 0); + free_image(im); + free_image(sized); + free_image(prmask); + if (filename) break; + } +} + + +void demo_isegmenter(char *datacfg, char *cfg, char *weights, int cam_index, const char *filename) +{ +#ifdef OPENCV + printf("Classifier Demo\n"); + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); + + srand(2222222); + void * cap = open_video_stream(filename, cam_index, 0,0,0); + + if(!cap) error("Couldn't connect to webcam.\n"); + float fps = 0; + + while(1){ + struct timeval tval_before, tval_after, tval_result; + gettimeofday(&tval_before, NULL); + + image in = get_image_from_stream(cap); + image in_s = letterbox_image(in, net->w, net->h); + + network_predict(net, in_s.data); + + printf("\033[2J"); + printf("\033[1;1H"); + printf("\nFPS:%.0f\n",fps); + + image pred = get_network_image(net); + image prmask = mask_to_rgb(pred); + show_image(prmask, "Segmenter", 10); + + free_image(in_s); + free_image(in); + free_image(prmask); + + gettimeofday(&tval_after, NULL); + timersub(&tval_after, &tval_before, &tval_result); + float curr = 1000000.f/((long int)tval_result.tv_usec); + fps = .9*fps + .1*curr; + } +#endif +} + + +void run_isegmenter(int argc, char **argv) +{ + if(argc < 4){ + fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); + return; + } + + char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); + int *gpus = 0; + int gpu = 0; + int ngpus = 0; + if(gpu_list){ + printf("%s\n", gpu_list); + int len = strlen(gpu_list); + ngpus = 1; + int i; + for(i = 0; i < len; ++i){ + if (gpu_list[i] == ',') ++ngpus; + } + gpus = calloc(ngpus, sizeof(int)); + for(i = 0; i < ngpus; ++i){ + gpus[i] = atoi(gpu_list); + gpu_list = strchr(gpu_list, ',')+1; + } + } else { + gpu = gpu_index; + gpus = &gpu; + ngpus = 1; + } + + int cam_index = find_int_arg(argc, argv, "-c", 0); + int clear = find_arg(argc, argv, "-clear"); + int display = find_arg(argc, argv, "-display"); + char *data = argv[3]; + char *cfg = argv[4]; + char *weights = (argc > 5) ? argv[5] : 0; + char *filename = (argc > 6) ? argv[6]: 0; + if(0==strcmp(argv[2], "test")) predict_isegmenter(data, cfg, weights, filename); + else if(0==strcmp(argv[2], "train")) train_isegmenter(data, cfg, weights, gpus, ngpus, clear, display); + else if(0==strcmp(argv[2], "demo")) demo_isegmenter(data, cfg, weights, cam_index, filename); +} + + diff --git a/image.darknet/inst/include/darknet/examples/lsd.c b/image.darknet/inst/include/darknet/examples/lsd.c new file mode 100644 index 0000000..4ab944c --- /dev/null +++ b/image.darknet/inst/include/darknet/examples/lsd.c @@ -0,0 +1,1378 @@ +#include +#include "darknet.h" + +/* +void train_lsd3(char *fcfg, char *fweight, char *gcfg, char *gweight, char *acfg, char *aweight, int clear) +{ +#ifdef GPU + //char *train_images = "/home/pjreddie/data/coco/trainvalno5k.txt"; + char *train_images = "/home/pjreddie/data/imagenet/imagenet1k.train.list"; + //char *style_images = "/home/pjreddie/data/coco/trainvalno5k.txt"; + char *style_images = "/home/pjreddie/zelda.txt"; + char *backup_directory = "/home/pjreddie/backup/"; + srand(time(0)); + network fnet = load_network(fcfg, fweight, clear); + network gnet = load_network(gcfg, gweight, clear); + network anet = load_network(acfg, aweight, clear); + char *gbase = basecfg(gcfg); + char *abase = basecfg(acfg); + + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", gnet->learning_rate, gnet->momentum, gnet->decay); + int imgs = gnet->batch*gnet->subdivisions; + int i = *gnet->seen/imgs; + data train, tbuffer; + data style, sbuffer; + + + list *slist = get_paths(style_images); + char **spaths = (char **)list_to_array(slist); + + list *tlist = get_paths(train_images); + char **tpaths = (char **)list_to_array(tlist); + + load_args targs= get_base_args(gnet); + targs.paths = tpaths; + targs.n = imgs; + targs.m = tlist->size; + targs.d = &tbuffer; + targs.type = CLASSIFICATION_DATA; + targs.classes = 1; + char *ls[1] = {"zelda"}; + targs.labels = ls; + + load_args sargs = get_base_args(gnet); + sargs.paths = spaths; + sargs.n = imgs; + sargs.m = slist->size; + sargs.d = &sbuffer; + sargs.type = CLASSIFICATION_DATA; + sargs.classes = 1; + sargs.labels = ls; + + pthread_t tload_thread = load_data_in_thread(targs); + pthread_t sload_thread = load_data_in_thread(sargs); + clock_t time; + + float aloss_avg = -1; + float floss_avg = -1; + + fnet->train=1; + int x_size = fnet->inputs*fnet->batch; + int y_size = fnet->truths*fnet->batch; + float *X = calloc(x_size, sizeof(float)); + float *y = calloc(y_size, sizeof(float)); + + + int ax_size = anet->inputs*anet->batch; + int ay_size = anet->truths*anet->batch; + fill_gpu(ay_size, .9, anet->truth_gpu, 1); + anet->delta_gpu = cuda_make_array(0, ax_size); + anet->train = 1; + + int gx_size = gnet->inputs*gnet->batch; + int gy_size = gnet->truths*gnet->batch; + gstate.input = cuda_make_array(0, gx_size); + gstate.truth = 0; + gstate.delta = 0; + gstate.train = 1; + + while (get_current_batch(gnet) < gnet->max_batches) { + i += 1; + time=clock(); + pthread_join(tload_thread, 0); + pthread_join(sload_thread, 0); + train = tbuffer; + style = sbuffer; + tload_thread = load_data_in_thread(targs); + sload_thread = load_data_in_thread(sargs); + + printf("Loaded: %lf seconds\n", sec(clock()-time)); + + data generated = copy_data(train); + time=clock(); + + int j, k; + float floss = 0; + for(j = 0; j < fnet->subdivisions; ++j){ + layer imlayer = gnet->layers[gnet->n - 1]; + get_next_batch(train, fnet->batch, j*fnet->batch, X, y); + + cuda_push_array(fstate.input, X, x_size); + cuda_push_array(gstate.input, X, gx_size); + *gnet->seen += gnet->batch; + + forward_network_gpu(fnet, fstate); + float *feats = fnet->layers[fnet->n - 2].output_gpu; + copy_gpu(y_size, feats, 1, fstate.truth, 1); + + forward_network_gpu(gnet, gstate); + float *gen = gnet->layers[gnet->n-1].output_gpu; + copy_gpu(x_size, gen, 1, fstate.input, 1); + + fill_gpu(x_size, 0, fstate.delta, 1); + forward_network_gpu(fnet, fstate); + backward_network_gpu(fnet, fstate); + //HERE + + astate.input = gen; + fill_gpu(ax_size, 0, astate.delta, 1); + forward_network_gpu(anet, astate); + backward_network_gpu(anet, astate); + + float *delta = imlayer.delta_gpu; + fill_gpu(x_size, 0, delta, 1); + scal_gpu(x_size, 100, astate.delta, 1); + scal_gpu(x_size, .001, fstate.delta, 1); + axpy_gpu(x_size, 1, fstate.delta, 1, delta, 1); + axpy_gpu(x_size, 1, astate.delta, 1, delta, 1); + + //fill_gpu(x_size, 0, delta, 1); + //cuda_push_array(delta, X, x_size); + //axpy_gpu(x_size, -1, imlayer.output_gpu, 1, delta, 1); + //printf("pix error: %f\n", cuda_mag_array(delta, x_size)); + printf("fea error: %f\n", cuda_mag_array(fstate.delta, x_size)); + printf("adv error: %f\n", cuda_mag_array(astate.delta, x_size)); + //axpy_gpu(x_size, 1, astate.delta, 1, delta, 1); + + backward_network_gpu(gnet, gstate); + + floss += get_network_cost(fnet) /(fnet->subdivisions*fnet->batch); + + cuda_pull_array(imlayer.output_gpu, imlayer.output, imlayer.outputs*imlayer.batch); + for(k = 0; k < gnet->batch; ++k){ + int index = j*gnet->batch + k; + copy_cpu(imlayer.outputs, imlayer.output + k*imlayer.outputs, 1, generated.X.vals[index], 1); + generated.y.vals[index][0] = .1; + style.y.vals[index][0] = .9; + } + } + +*/ +/* + image sim = float_to_image(anet->w, anet->h, anet->c, style.X.vals[j]); + show_image(sim, "style"); + cvWaitKey(0); + */ + /* + + harmless_update_network_gpu(anet); + + data merge = concat_data(style, generated); + randomize_data(merge); + float aloss = train_network(anet, merge); + + update_network_gpu(gnet); + + free_data(merge); + free_data(train); + free_data(generated); + free_data(style); + if (aloss_avg < 0) aloss_avg = aloss; + if (floss_avg < 0) floss_avg = floss; + aloss_avg = aloss_avg*.9 + aloss*.1; + floss_avg = floss_avg*.9 + floss*.1; + + printf("%d: gen: %f, adv: %f | gen_avg: %f, adv_avg: %f, %f rate, %lf seconds, %d images\n", i, floss, aloss, floss_avg, aloss_avg, get_current_rate(gnet), sec(clock()-time), i*imgs); + if(i%1000==0){ + char buff[256]; + sprintf(buff, "%s/%s_%d.weights", backup_directory, gbase, i); + save_weights(gnet, buff); + sprintf(buff, "%s/%s_%d.weights", backup_directory, abase, i); + save_weights(anet, buff); + } + if(i%100==0){ + char buff[256]; + sprintf(buff, "%s/%s.backup", backup_directory, gbase); + save_weights(gnet, buff); + sprintf(buff, "%s/%s.backup", backup_directory, abase); + save_weights(anet, buff); + } + } +#endif +} +*/ + +/* +void train_pix2pix(char *cfg, char *weight, char *acfg, char *aweight, int clear) +{ +#ifdef GPU + //char *train_images = "/home/pjreddie/data/coco/train1.txt"; + //char *train_images = "/home/pjreddie/data/coco/trainvalno5k.txt"; + char *train_images = "/home/pjreddie/data/imagenet/imagenet1k.train.list"; + char *backup_directory = "/home/pjreddie/backup/"; + srand(time(0)); + char *base = basecfg(cfg); + char *abase = basecfg(acfg); + printf("%s\n", base); + network net = load_network(cfg, weight, clear); + network anet = load_network(acfg, aweight, clear); + + int i, j, k; + layer imlayer = {0}; + for (i = 0; i < net->n; ++i) { + if (net->layers[i].out_c == 3) { + imlayer = net->layers[i]; + break; + } + } + + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + int imgs = net->batch*net->subdivisions; + i = *net->seen/imgs; + data train, buffer; + + + list *plist = get_paths(train_images); + //int N = plist->size; + char **paths = (char **)list_to_array(plist); + + load_args args = {0}; + args.w = net->w; + args.h = net->h; + args.paths = paths; + args.n = imgs; + args.m = plist->size; + args.d = &buffer; + + args.min = net->min_crop; + args.max = net->max_crop; + args.angle = net->angle; + args.aspect = net->aspect; + args.exposure = net->exposure; + args.saturation = net->saturation; + args.hue = net->hue; + args.size = net->w; + args.type = CLASSIFICATION_DATA; + args.classes = 1; + char *ls[1] = {"coco"}; + args.labels = ls; + + pthread_t load_thread = load_data_in_thread(args); + clock_t time; + + network_state gstate = {0}; + gstate.index = 0; + gstate.net = net; + int x_size = get_network_input_size(net)*net->batch; + int y_size = x_size; + gstate.input = cuda_make_array(0, x_size); + gstate.truth = cuda_make_array(0, y_size); + gstate.delta = 0; + gstate.train = 1; + float *pixs = calloc(x_size, sizeof(float)); + float *graypixs = calloc(x_size, sizeof(float)); + float *y = calloc(y_size, sizeof(float)); + + network_state astate = {0}; + astate.index = 0; + astate.net = anet; + int ay_size = get_network_output_size(anet)*anet->batch; + astate.input = 0; + astate.truth = 0; + astate.delta = 0; + astate.train = 1; + + float *imerror = cuda_make_array(0, imlayer.outputs); + float *ones_gpu = cuda_make_array(0, ay_size); + fill_gpu(ay_size, .9, ones_gpu, 1); + + float aloss_avg = -1; + float gloss_avg = -1; + + //data generated = copy_data(train); + + while (get_current_batch(net) < net->max_batches) { + i += 1; + time=clock(); + pthread_join(load_thread, 0); + train = buffer; + load_thread = load_data_in_thread(args); + + printf("Loaded: %lf seconds\n", sec(clock()-time)); + + data gray = copy_data(train); + for(j = 0; j < imgs; ++j){ + image gim = float_to_image(net->w, net->h, net->c, gray.X.vals[j]); + grayscale_image_3c(gim); + train.y.vals[j][0] = .9; + + image yim = float_to_image(net->w, net->h, net->c, train.X.vals[j]); + //rgb_to_yuv(yim); + } + time=clock(); + float gloss = 0; + + for(j = 0; j < net->subdivisions; ++j){ + get_next_batch(train, net->batch, j*net->batch, pixs, y); + get_next_batch(gray, net->batch, j*net->batch, graypixs, y); + cuda_push_array(gstate.input, graypixs, x_size); + cuda_push_array(gstate.truth, pixs, y_size); + */ + /* + image origi = float_to_image(net->w, net->h, 3, pixs); + image grayi = float_to_image(net->w, net->h, 3, graypixs); + show_image(grayi, "gray"); + show_image(origi, "orig"); + cvWaitKey(0); + */ + /* + *net->seen += net->batch; + forward_network_gpu(net, gstate); + + fill_gpu(imlayer.outputs, 0, imerror, 1); + astate.input = imlayer.output_gpu; + astate.delta = imerror; + astate.truth = ones_gpu; + forward_network_gpu(anet, astate); + backward_network_gpu(anet, astate); + + scal_gpu(imlayer.outputs, .1, net->layers[net->n-1].delta_gpu, 1); + + backward_network_gpu(net, gstate); + + scal_gpu(imlayer.outputs, 1000, imerror, 1); + + printf("realness %f\n", cuda_mag_array(imerror, imlayer.outputs)); + printf("features %f\n", cuda_mag_array(net->layers[net->n-1].delta_gpu, imlayer.outputs)); + + axpy_gpu(imlayer.outputs, 1, imerror, 1, imlayer.delta_gpu, 1); + + gloss += get_network_cost(net) /(net->subdivisions*net->batch); + + cuda_pull_array(imlayer.output_gpu, imlayer.output, imlayer.outputs*imlayer.batch); + for(k = 0; k < net->batch; ++k){ + int index = j*net->batch + k; + copy_cpu(imlayer.outputs, imlayer.output + k*imlayer.outputs, 1, gray.X.vals[index], 1); + gray.y.vals[index][0] = .1; + } + } + harmless_update_network_gpu(anet); + + data merge = concat_data(train, gray); + randomize_data(merge); + float aloss = train_network(anet, merge); + + update_network_gpu(net); + update_network_gpu(anet); + free_data(merge); + free_data(train); + free_data(gray); + if (aloss_avg < 0) aloss_avg = aloss; + aloss_avg = aloss_avg*.9 + aloss*.1; + gloss_avg = gloss_avg*.9 + gloss*.1; + + printf("%d: gen: %f, adv: %f | gen_avg: %f, adv_avg: %f, %f rate, %lf seconds, %d images\n", i, gloss, aloss, gloss_avg, aloss_avg, get_current_rate(net), sec(clock()-time), i*imgs); + if(i%1000==0){ + char buff[256]; + sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); + save_weights(net, buff); + sprintf(buff, "%s/%s_%d.weights", backup_directory, abase, i); + save_weights(anet, buff); + } + if(i%100==0){ + char buff[256]; + sprintf(buff, "%s/%s.backup", backup_directory, base); + save_weights(net, buff); + sprintf(buff, "%s/%s.backup", backup_directory, abase); + save_weights(anet, buff); + } + } + char buff[256]; + sprintf(buff, "%s/%s_final.weights", backup_directory, base); + save_weights(net, buff); +#endif +} +*/ + +void slerp(float *start, float *end, float s, int n, float *out) +{ + float omega = acos(dot_cpu(n, start, 1, end, 1)); + float so = sin(omega); + fill_cpu(n, 0, out, 1); + axpy_cpu(n, sin((1-s)*omega)/so, start, 1, out, 1); + axpy_cpu(n, sin(s*omega)/so, end, 1, out, 1); + + float mag = mag_array(out, n); + scale_array(out, n, 1./mag); +} + +image random_unit_vector_image(int w, int h, int c) +{ + image im = make_image(w, h, c); + int i; + for(i = 0; i < im.w*im.h*im.c; ++i){ + im.data[i] = rand_normal(); + } + float mag = mag_array(im.data, im.w*im.h*im.c); + scale_array(im.data, im.w*im.h*im.c, 1./mag); + return im; +} + +void inter_dcgan(char *cfgfile, char *weightfile) +{ + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + srand(2222222); + + clock_t time; + char buff[256]; + char *input = buff; + int i, imlayer = 0; + + for (i = 0; i < net->n; ++i) { + if (net->layers[i].out_c == 3) { + imlayer = i; + printf("%d\n", i); + break; + } + } + image start = random_unit_vector_image(net->w, net->h, net->c); + image end = random_unit_vector_image(net->w, net->h, net->c); + image im = make_image(net->w, net->h, net->c); + image orig = copy_image(start); + + int c = 0; + int count = 0; + int max_count = 15; + while(1){ + ++c; + + if(count == max_count){ + count = 0; + free_image(start); + start = end; + end = random_unit_vector_image(net->w, net->h, net->c); + if(c > 300){ + end = orig; + } + if(c>300 + max_count) return; + } + ++count; + + slerp(start.data, end.data, (float)count / max_count, im.w*im.h*im.c, im.data); + + float *X = im.data; + time=clock(); + network_predict(net, X); + image out = get_network_image_layer(net, imlayer); + //yuv_to_rgb(out); + normalize_image(out); + printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); + //char buff[256]; + sprintf(buff, "out%05d", c); + save_image(out, "out"); + save_image(out, buff); + show_image(out, "out", 0); + } +} + +void test_dcgan(char *cfgfile, char *weightfile) +{ + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + srand(2222222); + + clock_t time; + char buff[256]; + char *input = buff; + int imlayer = 0; + + imlayer = net->n-1; + + while(1){ + image im = make_image(net->w, net->h, net->c); + int i; + for(i = 0; i < im.w*im.h*im.c; ++i){ + im.data[i] = rand_normal(); + } + //float mag = mag_array(im.data, im.w*im.h*im.c); + //scale_array(im.data, im.w*im.h*im.c, 1./mag); + + float *X = im.data; + time=clock(); + network_predict(net, X); + image out = get_network_image_layer(net, imlayer); + //yuv_to_rgb(out); + normalize_image(out); + printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); + save_image(out, "out"); + show_image(out, "out", 0); + + free_image(im); + } +} + +void set_network_alpha_beta(network *net, float alpha, float beta) +{ + int i; + for(i = 0; i < net->n; ++i){ + if(net->layers[i].type == SHORTCUT){ + net->layers[i].alpha = alpha; + net->layers[i].beta = beta; + } + } +} + +void train_prog(char *cfg, char *weight, char *acfg, char *aweight, int clear, int display, char *train_images, int maxbatch) +{ +#ifdef GPU + char *backup_directory = "/home/pjreddie/backup/"; + srand(time(0)); + char *base = basecfg(cfg); + char *abase = basecfg(acfg); + printf("%s\n", base); + network *gnet = load_network(cfg, weight, clear); + network *anet = load_network(acfg, aweight, clear); + + int i, j, k; + layer imlayer = gnet->layers[gnet->n-1]; + + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", gnet->learning_rate, gnet->momentum, gnet->decay); + int imgs = gnet->batch*gnet->subdivisions; + i = *gnet->seen/imgs; + data train, buffer; + + + list *plist = get_paths(train_images); + char **paths = (char **)list_to_array(plist); + + load_args args= get_base_args(anet); + args.paths = paths; + args.n = imgs; + args.m = plist->size; + args.d = &buffer; + args.type = CLASSIFICATION_DATA; + args.threads=16; + args.classes = 1; + char *ls[2] = {"imagenet", "zzzzzzzz"}; + args.labels = ls; + + pthread_t load_thread = load_data_in_thread(args); + clock_t time; + + gnet->train = 1; + anet->train = 1; + + int x_size = gnet->inputs*gnet->batch; + int y_size = gnet->truths*gnet->batch; + float *imerror = cuda_make_array(0, y_size); + + float aloss_avg = -1; + + if (maxbatch == 0) maxbatch = gnet->max_batches; + while (get_current_batch(gnet) < maxbatch) { + { + int cb = get_current_batch(gnet); + float alpha = (float) cb / (maxbatch/2); + if(alpha > 1) alpha = 1; + float beta = 1 - alpha; + printf("%f %f\n", alpha, beta); + set_network_alpha_beta(gnet, alpha, beta); + set_network_alpha_beta(anet, beta, alpha); + } + + i += 1; + time=clock(); + pthread_join(load_thread, 0); + train = buffer; + + load_thread = load_data_in_thread(args); + + printf("Loaded: %lf seconds\n", sec(clock()-time)); + + data gen = copy_data(train); + for (j = 0; j < imgs; ++j) { + train.y.vals[j][0] = 1; + gen.y.vals[j][0] = 0; + } + time=clock(); + + for (j = 0; j < gnet->subdivisions; ++j) { + get_next_batch(train, gnet->batch, j*gnet->batch, gnet->truth, 0); + int z; + for(z = 0; z < x_size; ++z){ + gnet->input[z] = rand_normal(); + } + /* + for(z = 0; z < gnet->batch; ++z){ + float mag = mag_array(gnet->input + z*gnet->inputs, gnet->inputs); + scale_array(gnet->input + z*gnet->inputs, gnet->inputs, 1./mag); + } + */ + *gnet->seen += gnet->batch; + forward_network(gnet); + + fill_gpu(imlayer.outputs*imlayer.batch, 0, imerror, 1); + fill_cpu(anet->truths*anet->batch, 1, anet->truth, 1); + copy_cpu(anet->inputs*anet->batch, imlayer.output, 1, anet->input, 1); + anet->delta_gpu = imerror; + forward_network(anet); + backward_network(anet); + + //float genaloss = *anet->cost / anet->batch; + + scal_gpu(imlayer.outputs*imlayer.batch, 1, imerror, 1); + scal_gpu(imlayer.outputs*imlayer.batch, 0, gnet->layers[gnet->n-1].delta_gpu, 1); + + axpy_gpu(imlayer.outputs*imlayer.batch, 1, imerror, 1, gnet->layers[gnet->n-1].delta_gpu, 1); + + backward_network(gnet); + + for(k = 0; k < gnet->batch; ++k){ + int index = j*gnet->batch + k; + copy_cpu(gnet->outputs, gnet->output + k*gnet->outputs, 1, gen.X.vals[index], 1); + } + } + harmless_update_network_gpu(anet); + + data merge = concat_data(train, gen); + float aloss = train_network(anet, merge); + +#ifdef OPENCV + if(display){ + image im = float_to_image(anet->w, anet->h, anet->c, gen.X.vals[0]); + image im2 = float_to_image(anet->w, anet->h, anet->c, train.X.vals[0]); + show_image(im, "gen", 1); + show_image(im2, "train", 1); + save_image(im, "gen"); + save_image(im2, "train"); + } +#endif + + update_network_gpu(gnet); + + free_data(merge); + free_data(train); + free_data(gen); + if (aloss_avg < 0) aloss_avg = aloss; + aloss_avg = aloss_avg*.9 + aloss*.1; + + printf("%d: adv: %f | adv_avg: %f, %f rate, %lf seconds, %d images\n", i, aloss, aloss_avg, get_current_rate(gnet), sec(clock()-time), i*imgs); + if(i%10000==0){ + char buff[256]; + sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); + save_weights(gnet, buff); + sprintf(buff, "%s/%s_%d.weights", backup_directory, abase, i); + save_weights(anet, buff); + } + if(i%1000==0){ + char buff[256]; + sprintf(buff, "%s/%s.backup", backup_directory, base); + save_weights(gnet, buff); + sprintf(buff, "%s/%s.backup", backup_directory, abase); + save_weights(anet, buff); + } + } + char buff[256]; + sprintf(buff, "%s/%s_final.weights", backup_directory, base); + save_weights(gnet, buff); +#endif +} + +void train_dcgan(char *cfg, char *weight, char *acfg, char *aweight, int clear, int display, char *train_images, int maxbatch) +{ +#ifdef GPU + char *backup_directory = "/home/pjreddie/backup/"; + srand(time(0)); + char *base = basecfg(cfg); + char *abase = basecfg(acfg); + printf("%s\n", base); + network *gnet = load_network(cfg, weight, clear); + network *anet = load_network(acfg, aweight, clear); + //float orig_rate = anet->learning_rate; + + int i, j, k; + layer imlayer = {0}; + for (i = 0; i < gnet->n; ++i) { + if (gnet->layers[i].out_c == 3) { + imlayer = gnet->layers[i]; + break; + } + } + + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", gnet->learning_rate, gnet->momentum, gnet->decay); + int imgs = gnet->batch*gnet->subdivisions; + i = *gnet->seen/imgs; + data train, buffer; + + + list *plist = get_paths(train_images); + //int N = plist->size; + char **paths = (char **)list_to_array(plist); + + load_args args= get_base_args(anet); + args.paths = paths; + args.n = imgs; + args.m = plist->size; + args.d = &buffer; + args.type = CLASSIFICATION_DATA; + args.threads=16; + args.classes = 1; + char *ls[2] = {"imagenet", "zzzzzzzz"}; + args.labels = ls; + + pthread_t load_thread = load_data_in_thread(args); + clock_t time; + + gnet->train = 1; + anet->train = 1; + + int x_size = gnet->inputs*gnet->batch; + int y_size = gnet->truths*gnet->batch; + float *imerror = cuda_make_array(0, y_size); + + //int ay_size = anet->truths*anet->batch; + + float aloss_avg = -1; + + //data generated = copy_data(train); + + if (maxbatch == 0) maxbatch = gnet->max_batches; + while (get_current_batch(gnet) < maxbatch) { + i += 1; + time=clock(); + pthread_join(load_thread, 0); + train = buffer; + + //translate_data_rows(train, -.5); + //scale_data_rows(train, 2); + + load_thread = load_data_in_thread(args); + + printf("Loaded: %lf seconds\n", sec(clock()-time)); + + data gen = copy_data(train); + for (j = 0; j < imgs; ++j) { + train.y.vals[j][0] = 1; + gen.y.vals[j][0] = 0; + } + time=clock(); + + for(j = 0; j < gnet->subdivisions; ++j){ + get_next_batch(train, gnet->batch, j*gnet->batch, gnet->truth, 0); + int z; + for(z = 0; z < x_size; ++z){ + gnet->input[z] = rand_normal(); + } + for(z = 0; z < gnet->batch; ++z){ + float mag = mag_array(gnet->input + z*gnet->inputs, gnet->inputs); + scale_array(gnet->input + z*gnet->inputs, gnet->inputs, 1./mag); + } + /* + for(z = 0; z < 100; ++z){ + printf("%f, ", gnet->input[z]); + } + printf("\n"); + printf("input: %f %f\n", mean_array(gnet->input, x_size), variance_array(gnet->input, x_size)); + */ + + //cuda_push_array(gnet->input_gpu, gnet->input, x_size); + //cuda_push_array(gnet->truth_gpu, gnet->truth, y_size); + *gnet->seen += gnet->batch; + forward_network(gnet); + + fill_gpu(imlayer.outputs*imlayer.batch, 0, imerror, 1); + fill_cpu(anet->truths*anet->batch, 1, anet->truth, 1); + copy_cpu(anet->inputs*anet->batch, imlayer.output, 1, anet->input, 1); + anet->delta_gpu = imerror; + forward_network(anet); + backward_network(anet); + + //float genaloss = *anet->cost / anet->batch; + //printf("%f\n", genaloss); + + scal_gpu(imlayer.outputs*imlayer.batch, 1, imerror, 1); + scal_gpu(imlayer.outputs*imlayer.batch, 0, gnet->layers[gnet->n-1].delta_gpu, 1); + + //printf("realness %f\n", cuda_mag_array(imerror, imlayer.outputs*imlayer.batch)); + //printf("features %f\n", cuda_mag_array(gnet->layers[gnet->n-1].delta_gpu, imlayer.outputs*imlayer.batch)); + + axpy_gpu(imlayer.outputs*imlayer.batch, 1, imerror, 1, gnet->layers[gnet->n-1].delta_gpu, 1); + + backward_network(gnet); + + /* + for(k = 0; k < gnet->n; ++k){ + layer l = gnet->layers[k]; + cuda_pull_array(l.output_gpu, l.output, l.outputs*l.batch); + printf("%d: %f %f\n", k, mean_array(l.output, l.outputs*l.batch), variance_array(l.output, l.outputs*l.batch)); + } + */ + + for(k = 0; k < gnet->batch; ++k){ + int index = j*gnet->batch + k; + copy_cpu(gnet->outputs, gnet->output + k*gnet->outputs, 1, gen.X.vals[index], 1); + } + } + harmless_update_network_gpu(anet); + + data merge = concat_data(train, gen); + //randomize_data(merge); + float aloss = train_network(anet, merge); + + //translate_image(im, 1); + //scale_image(im, .5); + //translate_image(im2, 1); + //scale_image(im2, .5); +#ifdef OPENCV + if(display){ + image im = float_to_image(anet->w, anet->h, anet->c, gen.X.vals[0]); + image im2 = float_to_image(anet->w, anet->h, anet->c, train.X.vals[0]); + show_image(im, "gen", 1); + show_image(im2, "train", 1); + save_image(im, "gen"); + save_image(im2, "train"); + } +#endif + + /* + if(aloss < .1){ + anet->learning_rate = 0; + } else if (aloss > .3){ + anet->learning_rate = orig_rate; + } + */ + + update_network_gpu(gnet); + + free_data(merge); + free_data(train); + free_data(gen); + if (aloss_avg < 0) aloss_avg = aloss; + aloss_avg = aloss_avg*.9 + aloss*.1; + + printf("%d: adv: %f | adv_avg: %f, %f rate, %lf seconds, %d images\n", i, aloss, aloss_avg, get_current_rate(gnet), sec(clock()-time), i*imgs); + if(i%10000==0){ + char buff[256]; + sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); + save_weights(gnet, buff); + sprintf(buff, "%s/%s_%d.weights", backup_directory, abase, i); + save_weights(anet, buff); + } + if(i%1000==0){ + char buff[256]; + sprintf(buff, "%s/%s.backup", backup_directory, base); + save_weights(gnet, buff); + sprintf(buff, "%s/%s.backup", backup_directory, abase); + save_weights(anet, buff); + } + } + char buff[256]; + sprintf(buff, "%s/%s_final.weights", backup_directory, base); + save_weights(gnet, buff); +#endif +} + +void train_colorizer(char *cfg, char *weight, char *acfg, char *aweight, int clear, int display) +{ +#ifdef GPU + //char *train_images = "/home/pjreddie/data/coco/train1.txt"; + //char *train_images = "/home/pjreddie/data/coco/trainvalno5k.txt"; + char *train_images = "/home/pjreddie/data/imagenet/imagenet1k.train.list"; + char *backup_directory = "/home/pjreddie/backup/"; + srand(time(0)); + char *base = basecfg(cfg); + char *abase = basecfg(acfg); + printf("%s\n", base); + network *net = load_network(cfg, weight, clear); + network *anet = load_network(acfg, aweight, clear); + + int i, j, k; + layer imlayer = {0}; + for (i = 0; i < net->n; ++i) { + if (net->layers[i].out_c == 3) { + imlayer = net->layers[i]; + break; + } + } + + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + int imgs = net->batch*net->subdivisions; + i = *net->seen/imgs; + data train, buffer; + + + list *plist = get_paths(train_images); + //int N = plist->size; + char **paths = (char **)list_to_array(plist); + + load_args args= get_base_args(net); + args.paths = paths; + args.n = imgs; + args.m = plist->size; + args.d = &buffer; + + args.type = CLASSIFICATION_DATA; + args.classes = 1; + char *ls[2] = {"imagenet"}; + args.labels = ls; + + pthread_t load_thread = load_data_in_thread(args); + clock_t time; + + int x_size = net->inputs*net->batch; + //int y_size = x_size; + net->delta = 0; + net->train = 1; + float *pixs = calloc(x_size, sizeof(float)); + float *graypixs = calloc(x_size, sizeof(float)); + //float *y = calloc(y_size, sizeof(float)); + + //int ay_size = anet->outputs*anet->batch; + anet->delta = 0; + anet->train = 1; + + float *imerror = cuda_make_array(0, imlayer.outputs*imlayer.batch); + + float aloss_avg = -1; + float gloss_avg = -1; + + //data generated = copy_data(train); + + while (get_current_batch(net) < net->max_batches) { + i += 1; + time=clock(); + pthread_join(load_thread, 0); + train = buffer; + load_thread = load_data_in_thread(args); + + printf("Loaded: %lf seconds\n", sec(clock()-time)); + + data gray = copy_data(train); + for(j = 0; j < imgs; ++j){ + image gim = float_to_image(net->w, net->h, net->c, gray.X.vals[j]); + grayscale_image_3c(gim); + train.y.vals[j][0] = .95; + gray.y.vals[j][0] = .05; + } + time=clock(); + float gloss = 0; + + for(j = 0; j < net->subdivisions; ++j){ + get_next_batch(train, net->batch, j*net->batch, pixs, 0); + get_next_batch(gray, net->batch, j*net->batch, graypixs, 0); + cuda_push_array(net->input_gpu, graypixs, net->inputs*net->batch); + cuda_push_array(net->truth_gpu, pixs, net->truths*net->batch); + /* + image origi = float_to_image(net->w, net->h, 3, pixs); + image grayi = float_to_image(net->w, net->h, 3, graypixs); + show_image(grayi, "gray"); + show_image(origi, "orig"); + cvWaitKey(0); + */ + *net->seen += net->batch; + forward_network_gpu(net); + + fill_gpu(imlayer.outputs*imlayer.batch, 0, imerror, 1); + copy_gpu(anet->inputs*anet->batch, imlayer.output_gpu, 1, anet->input_gpu, 1); + fill_gpu(anet->inputs*anet->batch, .95, anet->truth_gpu, 1); + anet->delta_gpu = imerror; + forward_network_gpu(anet); + backward_network_gpu(anet); + + scal_gpu(imlayer.outputs*imlayer.batch, 1./100., net->layers[net->n-1].delta_gpu, 1); + + scal_gpu(imlayer.outputs*imlayer.batch, 1, imerror, 1); + + printf("realness %f\n", cuda_mag_array(imerror, imlayer.outputs*imlayer.batch)); + printf("features %f\n", cuda_mag_array(net->layers[net->n-1].delta_gpu, imlayer.outputs*imlayer.batch)); + + axpy_gpu(imlayer.outputs*imlayer.batch, 1, imerror, 1, net->layers[net->n-1].delta_gpu, 1); + + backward_network_gpu(net); + + + gloss += *net->cost /(net->subdivisions*net->batch); + + for(k = 0; k < net->batch; ++k){ + int index = j*net->batch + k; + copy_cpu(imlayer.outputs, imlayer.output + k*imlayer.outputs, 1, gray.X.vals[index], 1); + } + } + harmless_update_network_gpu(anet); + + data merge = concat_data(train, gray); + //randomize_data(merge); + float aloss = train_network(anet, merge); + + update_network_gpu(net); + +#ifdef OPENCV + if(display){ + image im = float_to_image(anet->w, anet->h, anet->c, gray.X.vals[0]); + image im2 = float_to_image(anet->w, anet->h, anet->c, train.X.vals[0]); + show_image(im, "gen", 1); + show_image(im2, "train", 1); + } +#endif + free_data(merge); + free_data(train); + free_data(gray); + if (aloss_avg < 0) aloss_avg = aloss; + aloss_avg = aloss_avg*.9 + aloss*.1; + gloss_avg = gloss_avg*.9 + gloss*.1; + + printf("%d: gen: %f, adv: %f | gen_avg: %f, adv_avg: %f, %f rate, %lf seconds, %d images\n", i, gloss, aloss, gloss_avg, aloss_avg, get_current_rate(net), sec(clock()-time), i*imgs); + if(i%1000==0){ + char buff[256]; + sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); + save_weights(net, buff); + sprintf(buff, "%s/%s_%d.weights", backup_directory, abase, i); + save_weights(anet, buff); + } + if(i%100==0){ + char buff[256]; + sprintf(buff, "%s/%s.backup", backup_directory, base); + save_weights(net, buff); + sprintf(buff, "%s/%s.backup", backup_directory, abase); + save_weights(anet, buff); + } + } + char buff[256]; + sprintf(buff, "%s/%s_final.weights", backup_directory, base); + save_weights(net, buff); +#endif +} + +/* + void train_lsd2(char *cfgfile, char *weightfile, char *acfgfile, char *aweightfile, int clear) + { +#ifdef GPU +char *train_images = "/home/pjreddie/data/coco/trainvalno5k.txt"; +char *backup_directory = "/home/pjreddie/backup/"; +srand(time(0)); +char *base = basecfg(cfgfile); +printf("%s\n", base); +network net = parse_network_cfg(cfgfile); +if(weightfile){ +load_weights(&net, weightfile); +} +if(clear) *net->seen = 0; + +char *abase = basecfg(acfgfile); +network anet = parse_network_cfg(acfgfile); +if(aweightfile){ +load_weights(&anet, aweightfile); +} +if(clear) *anet->seen = 0; + +int i, j, k; +layer imlayer = {0}; +for (i = 0; i < net->n; ++i) { +if (net->layers[i].out_c == 3) { +imlayer = net->layers[i]; +break; +} +} + +printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); +int imgs = net->batch*net->subdivisions; +i = *net->seen/imgs; +data train, buffer; + + +list *plist = get_paths(train_images); +//int N = plist->size; +char **paths = (char **)list_to_array(plist); + +load_args args = {0}; +args.w = net->w; +args.h = net->h; +args.paths = paths; +args.n = imgs; +args.m = plist->size; +args.d = &buffer; + +args.min = net->min_crop; +args.max = net->max_crop; +args.angle = net->angle; +args.aspect = net->aspect; +args.exposure = net->exposure; +args.saturation = net->saturation; +args.hue = net->hue; +args.size = net->w; +args.type = CLASSIFICATION_DATA; +args.classes = 1; +char *ls[1] = {"coco"}; +args.labels = ls; + +pthread_t load_thread = load_data_in_thread(args); +clock_t time; + +network_state gstate = {0}; +gstate.index = 0; +gstate.net = net; +int x_size = get_network_input_size(net)*net->batch; +int y_size = 1*net->batch; +gstate.input = cuda_make_array(0, x_size); +gstate.truth = 0; +gstate.delta = 0; +gstate.train = 1; +float *X = calloc(x_size, sizeof(float)); +float *y = calloc(y_size, sizeof(float)); + +network_state astate = {0}; +astate.index = 0; +astate.net = anet; +int ay_size = get_network_output_size(anet)*anet->batch; +astate.input = 0; +astate.truth = 0; +astate.delta = 0; +astate.train = 1; + +float *imerror = cuda_make_array(0, imlayer.outputs); +float *ones_gpu = cuda_make_array(0, ay_size); +fill_gpu(ay_size, 1, ones_gpu, 1); + +float aloss_avg = -1; +float gloss_avg = -1; + +//data generated = copy_data(train); + +while (get_current_batch(net) < net->max_batches) { + i += 1; + time=clock(); + pthread_join(load_thread, 0); + train = buffer; + load_thread = load_data_in_thread(args); + + printf("Loaded: %lf seconds\n", sec(clock()-time)); + + data generated = copy_data(train); + time=clock(); + float gloss = 0; + + for(j = 0; j < net->subdivisions; ++j){ + get_next_batch(train, net->batch, j*net->batch, X, y); + cuda_push_array(gstate.input, X, x_size); + *net->seen += net->batch; + forward_network_gpu(net, gstate); + + fill_gpu(imlayer.outputs, 0, imerror, 1); + astate.input = imlayer.output_gpu; + astate.delta = imerror; + astate.truth = ones_gpu; + forward_network_gpu(anet, astate); + backward_network_gpu(anet, astate); + + scal_gpu(imlayer.outputs, 1, imerror, 1); + axpy_gpu(imlayer.outputs, 1, imerror, 1, imlayer.delta_gpu, 1); + + backward_network_gpu(net, gstate); + + printf("features %f\n", cuda_mag_array(imlayer.delta_gpu, imlayer.outputs)); + printf("realness %f\n", cuda_mag_array(imerror, imlayer.outputs)); + + gloss += get_network_cost(net) /(net->subdivisions*net->batch); + + cuda_pull_array(imlayer.output_gpu, imlayer.output, imlayer.outputs*imlayer.batch); + for(k = 0; k < net->batch; ++k){ + int index = j*net->batch + k; + copy_cpu(imlayer.outputs, imlayer.output + k*imlayer.outputs, 1, generated.X.vals[index], 1); + generated.y.vals[index][0] = 0; + } + } + harmless_update_network_gpu(anet); + + data merge = concat_data(train, generated); + randomize_data(merge); + float aloss = train_network(anet, merge); + + update_network_gpu(net); + update_network_gpu(anet); + free_data(merge); + free_data(train); + free_data(generated); + if (aloss_avg < 0) aloss_avg = aloss; + aloss_avg = aloss_avg*.9 + aloss*.1; + gloss_avg = gloss_avg*.9 + gloss*.1; + + printf("%d: gen: %f, adv: %f | gen_avg: %f, adv_avg: %f, %f rate, %lf seconds, %d images\n", i, gloss, aloss, gloss_avg, aloss_avg, get_current_rate(net), sec(clock()-time), i*imgs); + if(i%1000==0){ + char buff[256]; + sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); + save_weights(net, buff); + sprintf(buff, "%s/%s_%d.weights", backup_directory, abase, i); + save_weights(anet, buff); + } + if(i%100==0){ + char buff[256]; + sprintf(buff, "%s/%s.backup", backup_directory, base); + save_weights(net, buff); + sprintf(buff, "%s/%s.backup", backup_directory, abase); + save_weights(anet, buff); + } +} +char buff[256]; +sprintf(buff, "%s/%s_final.weights", backup_directory, base); +save_weights(net, buff); +#endif +} +*/ + +/* + void train_lsd(char *cfgfile, char *weightfile, int clear) + { + char *train_images = "/home/pjreddie/data/coco/trainvalno5k.txt"; + char *backup_directory = "/home/pjreddie/backup/"; + srand(time(0)); + char *base = basecfg(cfgfile); + printf("%s\n", base); + float avg_loss = -1; + network net = parse_network_cfg(cfgfile); + if(weightfile){ + load_weights(&net, weightfile); + } + if(clear) *net->seen = 0; + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + int imgs = net->batch*net->subdivisions; + int i = *net->seen/imgs; + data train, buffer; + + + list *plist = get_paths(train_images); +//int N = plist->size; +char **paths = (char **)list_to_array(plist); + +load_args args = {0}; +args.w = net->w; +args.h = net->h; +args.paths = paths; +args.n = imgs; +args.m = plist->size; +args.d = &buffer; + +args.min = net->min_crop; +args.max = net->max_crop; +args.angle = net->angle; +args.aspect = net->aspect; +args.exposure = net->exposure; +args.saturation = net->saturation; +args.hue = net->hue; +args.size = net->w; +args.type = CLASSIFICATION_DATA; +args.classes = 1; +char *ls[1] = {"coco"}; +args.labels = ls; + +pthread_t load_thread = load_data_in_thread(args); +clock_t time; +//while(i*imgs < N*120){ +while(get_current_batch(net) < net->max_batches){ +i += 1; +time=clock(); +pthread_join(load_thread, 0); +train = buffer; +load_thread = load_data_in_thread(args); + +printf("Loaded: %lf seconds\n", sec(clock()-time)); + +time=clock(); +float loss = train_network(net, train); +if (avg_loss < 0) avg_loss = loss; +avg_loss = avg_loss*.9 + loss*.1; + +printf("%d: %f, %f avg, %f rate, %lf seconds, %d images\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time), i*imgs); +if(i%1000==0){ +char buff[256]; +sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); +save_weights(net, buff); +} +if(i%100==0){ +char buff[256]; +sprintf(buff, "%s/%s.backup", backup_directory, base); +save_weights(net, buff); +} +free_data(train); +} +char buff[256]; +sprintf(buff, "%s/%s_final.weights", backup_directory, base); +save_weights(net, buff); +} +*/ + +void test_lsd(char *cfg, char *weights, char *filename, int gray) +{ + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); + srand(2222222); + + clock_t time; + char buff[256]; + char *input = buff; + int i, imlayer = 0; + + for (i = 0; i < net->n; ++i) { + if (net->layers[i].out_c == 3) { + imlayer = i; + printf("%d\n", i); + break; + } + } + + 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, "\n"); + } + image im = load_image_color(input, 0, 0); + image resized = resize_min(im, net->w); + image crop = crop_image(resized, (resized.w - net->w)/2, (resized.h - net->h)/2, net->w, net->h); + if(gray) grayscale_image_3c(crop); + + float *X = crop.data; + time=clock(); + network_predict(net, X); + image out = get_network_image_layer(net, imlayer); + //yuv_to_rgb(out); + constrain_image(out); + printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); + save_image(out, "out"); + show_image(out, "out", 1); + show_image(crop, "crop", 0); + + free_image(im); + free_image(resized); + free_image(crop); + if (filename) break; + } +} + + +void run_lsd(int argc, char **argv) +{ + if(argc < 4){ + fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); + return; + } + + int clear = find_arg(argc, argv, "-clear"); + int display = find_arg(argc, argv, "-display"); + int batches = find_int_arg(argc, argv, "-b", 0); + char *file = find_char_arg(argc, argv, "-file", "/home/pjreddie/data/imagenet/imagenet1k.train.list"); + + char *cfg = argv[3]; + char *weights = (argc > 4) ? argv[4] : 0; + char *filename = (argc > 5) ? argv[5] : 0; + char *acfg = argv[5]; + char *aweights = (argc > 6) ? argv[6] : 0; + //if(0==strcmp(argv[2], "train")) train_lsd(cfg, weights, clear); + //else if(0==strcmp(argv[2], "train2")) train_lsd2(cfg, weights, acfg, aweights, clear); + //else if(0==strcmp(argv[2], "traincolor")) train_colorizer(cfg, weights, acfg, aweights, clear); + //else if(0==strcmp(argv[2], "train3")) train_lsd3(argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], clear); + if(0==strcmp(argv[2], "traingan")) train_dcgan(cfg, weights, acfg, aweights, clear, display, file, batches); + else if(0==strcmp(argv[2], "trainprog")) train_prog(cfg, weights, acfg, aweights, clear, display, file, batches); + else if(0==strcmp(argv[2], "traincolor")) train_colorizer(cfg, weights, acfg, aweights, clear, display); + else if(0==strcmp(argv[2], "gan")) test_dcgan(cfg, weights); + else if(0==strcmp(argv[2], "inter")) inter_dcgan(cfg, weights); + else if(0==strcmp(argv[2], "test")) test_lsd(cfg, weights, filename, 0); + else if(0==strcmp(argv[2], "color")) test_lsd(cfg, weights, filename, 1); + /* + else if(0==strcmp(argv[2], "valid")) validate_lsd(cfg, weights); + */ +} diff --git a/image.darknet/inst/include/darknet/src/nightmare.c b/image.darknet/inst/include/darknet/examples/nightmare.c similarity index 58% rename from image.darknet/inst/include/darknet/src/nightmare.c rename to image.darknet/inst/include/darknet/examples/nightmare.c index ec7166c..2978eb6 100644 --- a/image.darknet/inst/include/darknet/src/nightmare.c +++ b/image.darknet/inst/include/darknet/examples/nightmare.c @@ -1,12 +1,6 @@ +#include "darknet.h" -#include "network.h" -#include "parser.h" -#include "blas.h" -#include "utils.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif +#include // ./darknet nightmare cfg/extractor.recon.cfg ~/trained/yolo-coco.conv frame6.png -reconstruct -iters 500 -i 3 -lambda .1 -rate .01 -smooth 2 @@ -51,31 +45,30 @@ void optimize_picture(network *net, image orig, int max_layer, float scale, floa image delta = make_image(im.w, im.h, im.c); - network_state state = {0}; - #ifdef GPU - state.input = cuda_make_array(im.data, im.w*im.h*im.c); - state.delta = cuda_make_array(im.data, im.w*im.h*im.c); + net->delta_gpu = cuda_make_array(delta.data, im.w*im.h*im.c); + copy_cpu(net->inputs, im.data, 1, net->input, 1); - forward_network_gpu(*net, state); - copy_ongpu(last.outputs, last.output_gpu, 1, last.delta_gpu, 1); + forward_network_gpu(net); + copy_gpu(last.outputs, last.output_gpu, 1, last.delta_gpu, 1); cuda_pull_array(last.delta_gpu, last.delta, last.outputs); calculate_loss(last.delta, last.delta, last.outputs, thresh); cuda_push_array(last.delta_gpu, last.delta, last.outputs); - backward_network_gpu(*net, state); + backward_network_gpu(net); - cuda_pull_array(state.delta, delta.data, im.w*im.h*im.c); - cuda_free(state.input); - cuda_free(state.delta); + cuda_pull_array(net->delta_gpu, delta.data, im.w*im.h*im.c); + cuda_free(net->delta_gpu); + net->delta_gpu = 0; #else - state.input = im.data; - state.delta = delta.data; - forward_network(*net, state); + printf("\nnet: %d %d %d im: %d %d %d\n", net->w, net->h, net->inputs, im.w, im.h, im.c); + copy_cpu(net->inputs, im.data, 1, net->input, 1); + net->delta = delta.data; + forward_network(net); copy_cpu(last.outputs, last.output, 1, last.delta, 1); calculate_loss(last.output, last.delta, last.outputs, thresh); - backward_network(*net, state); + backward_network(net); #endif if(flip) flip_image(delta); @@ -90,6 +83,10 @@ void optimize_picture(network *net, image orig, int max_layer, float scale, floa */ //rate = rate / abs_mean(out.data, out.w*out.h*out.c); + image gray = make_image(out.w, out.h, out.c); + fill_image(gray, .5); + axpy_cpu(orig.w*orig.h*orig.c, -1, orig.data, 1, gray.data, 1); + axpy_cpu(orig.w*orig.h*orig.c, .1, gray.data, 1, out.data, 1); if(norm) normalize_array(out.data, out.w*out.h*out.c); axpy_cpu(orig.w*orig.h*orig.c, rate, out.data, 1, orig.data, 1); @@ -135,42 +132,44 @@ void smooth(image recon, image update, float lambda, int num) } } -void reconstruct_picture(network net, float *features, image recon, image update, float rate, float momentum, float lambda, int smooth_size, int iters) +void reconstruct_picture(network *net, float *features, image recon, image update, float rate, float momentum, float lambda, int smooth_size, int iters) { int iter = 0; for (iter = 0; iter < iters; ++iter) { image delta = make_image(recon.w, recon.h, recon.c); - network_state state = {0}; #ifdef GPU - state.input = cuda_make_array(recon.data, recon.w*recon.h*recon.c); - state.delta = cuda_make_array(delta.data, delta.w*delta.h*delta.c); - state.truth = cuda_make_array(features, get_network_output_size(net)); + layer l = get_network_output_layer(net); + cuda_push_array(net->input_gpu, recon.data, recon.w*recon.h*recon.c); + //cuda_push_array(net->truth_gpu, features, net->truths); + net->delta_gpu = cuda_make_array(delta.data, delta.w*delta.h*delta.c); - forward_network_gpu(net, state); - backward_network_gpu(net, state); + forward_network_gpu(net); + cuda_push_array(l.delta_gpu, features, l.outputs); + axpy_gpu(l.outputs, -1, l.output_gpu, 1, l.delta_gpu, 1); + backward_network_gpu(net); - cuda_pull_array(state.delta, delta.data, delta.w*delta.h*delta.c); + cuda_pull_array(net->delta_gpu, delta.data, delta.w*delta.h*delta.c); - cuda_free(state.input); - cuda_free(state.delta); - cuda_free(state.truth); + cuda_free(net->delta_gpu); #else - state.input = recon.data; - state.delta = delta.data; - state.truth = features; + net->input = recon.data; + net->delta = delta.data; + net->truth = features; - forward_network(net, state); - backward_network(net, state); + forward_network(net); + backward_network(net); #endif + //normalize_array(delta.data, delta.w*delta.h*delta.c); axpy_cpu(recon.w*recon.h*recon.c, 1, delta.data, 1, update.data, 1); - smooth(recon, update, lambda, smooth_size); + //smooth(recon, update, lambda, smooth_size); axpy_cpu(recon.w*recon.h*recon.c, rate, update.data, 1, recon.data, 1); scal_cpu(recon.w*recon.h*recon.c, momentum, update.data, 1); - //float mag = mag_array(recon.data, recon.w*recon.h*recon.c); + float mag = mag_array(delta.data, recon.w*recon.h*recon.c); + printf("mag: %f\n", mag); //scal_cpu(recon.w*recon.h*recon.c, 600/mag, recon.data, 1); constrain_image(recon); @@ -178,6 +177,113 @@ void reconstruct_picture(network net, float *features, image recon, image update } } +/* +void run_lsd(int argc, char **argv) +{ + srand(0); + if(argc < 3){ + fprintf(stderr, "usage: %s %s [cfg] [weights] [image] [options! (optional)]\n", argv[0], argv[1]); + return; + } + + char *cfg = argv[2]; + char *weights = argv[3]; + char *input = argv[4]; + + int norm = find_int_arg(argc, argv, "-norm", 1); + int rounds = find_int_arg(argc, argv, "-rounds", 1); + int iters = find_int_arg(argc, argv, "-iters", 10); + float rate = find_float_arg(argc, argv, "-rate", .04); + float momentum = find_float_arg(argc, argv, "-momentum", .9); + float lambda = find_float_arg(argc, argv, "-lambda", .01); + char *prefix = find_char_arg(argc, argv, "-prefix", 0); + int reconstruct = find_arg(argc, argv, "-reconstruct"); + int smooth_size = find_int_arg(argc, argv, "-smooth", 1); + + network net = parse_network_cfg(cfg); + load_weights(&net, weights); + char *cfgbase = basecfg(cfg); + char *imbase = basecfg(input); + + set_batch_network(&net, 1); + image im = load_image_color(input, 0, 0); + + float *features = 0; + image update; + if (reconstruct){ + im = letterbox_image(im, net->w, net->h); + + int zz = 0; + network_predict(net, im.data); + image out_im = get_network_image(net); + image crop = crop_image(out_im, zz, zz, out_im.w-2*zz, out_im.h-2*zz); + //flip_image(crop); + image f_im = resize_image(crop, out_im.w, out_im.h); + free_image(crop); + printf("%d features\n", out_im.w*out_im.h*out_im.c); + + + im = resize_image(im, im.w, im.h); + f_im = resize_image(f_im, f_im.w, f_im.h); + features = f_im.data; + + int i; + for(i = 0; i < 14*14*512; ++i){ + features[i] += rand_uniform(-.19, .19); + } + + free_image(im); + im = make_random_image(im.w, im.h, im.c); + update = make_image(im.w, im.h, im.c); + + } + + int e; + int n; + for(e = 0; e < rounds; ++e){ + fprintf(stderr, "Iteration: "); + fflush(stderr); + for(n = 0; n < iters; ++n){ + fprintf(stderr, "%d, ", n); + fflush(stderr); + if(reconstruct){ + reconstruct_picture(net, features, im, update, rate, momentum, lambda, smooth_size, 1); + //if ((n+1)%30 == 0) rate *= .5; + show_image(im, "reconstruction"); +#ifdef OPENCV + cvWaitKey(10); +#endif + }else{ + int layer = max_layer + rand()%range - range/2; + int octave = rand()%octaves; + optimize_picture(&net, im, layer, 1/pow(1.33333333, octave), rate, thresh, norm); + } + } + fprintf(stderr, "done\n"); + char buff[256]; + if (prefix){ + sprintf(buff, "%s/%s_%s_%d_%06d",prefix, imbase, cfgbase, max_layer, e); + }else{ + sprintf(buff, "%s_%s_%d_%06d",imbase, cfgbase, max_layer, e); + } + printf("%d %s\n", e, buff); + save_image(im, buff); + //show_image(im, buff); + //cvWaitKey(0); + + if(rotate){ + image rot = rotate_image(im, rotate); + free_image(im); + im = rot; + } + image crop = crop_image(im, im.w * (1. - zoom)/2., im.h * (1.-zoom)/2., im.w*zoom, im.h*zoom); + image resized = resize_image(crop, im.w, im.h); + free_image(im); + free_image(crop); + im = resized; + } +} +*/ void run_nightmare(int argc, char **argv) { @@ -207,12 +313,11 @@ void run_nightmare(int argc, char **argv) int reconstruct = find_arg(argc, argv, "-reconstruct"); int smooth_size = find_int_arg(argc, argv, "-smooth", 1); - network net = parse_network_cfg(cfg); - load_weights(&net, weights); + network *net = load_network(cfg, weights, 0); char *cfgbase = basecfg(cfg); char *imbase = basecfg(input); - set_batch_network(&net, 1); + set_batch_network(net, 1); image im = load_image_color(input, 0, 0); if(0){ float scale = 1; @@ -224,35 +329,40 @@ void run_nightmare(int argc, char **argv) free_image(im); im = resized; } + //im = letterbox_image(im, net->w, net->h); float *features = 0; image update; if (reconstruct){ - resize_network(&net, im.w, im.h); + net->n = max_layer; + im = letterbox_image(im, net->w, net->h); + //resize_network(&net, im.w, im.h); - int zz = 0; network_predict(net, im.data); - image out_im = get_network_image(net); - image crop = crop_image(out_im, zz, zz, out_im.w-2*zz, out_im.h-2*zz); + if(net->layers[net->n-1].type == REGION){ + printf("region!\n"); + zero_objectness(net->layers[net->n-1]); + } + image out_im = copy_image(get_network_image(net)); + /* + image crop = crop_image(out_im, zz, zz, out_im.w-2*zz, out_im.h-2*zz); //flip_image(crop); image f_im = resize_image(crop, out_im.w, out_im.h); free_image(crop); + */ printf("%d features\n", out_im.w*out_im.h*out_im.c); + features = out_im.data; - im = resize_image(im, im.w, im.h); - f_im = resize_image(f_im, f_im.w, f_im.h); - features = f_im.data; - + /* int i; - for(i = 0; i < 14*14*512; ++i){ - features[i] += rand_uniform(-.19, .19); + for(i = 0; i < 14*14*512; ++i){ + //features[i] += rand_uniform(-.19, .19); } - free_image(im); im = make_random_image(im.w, im.h, im.c); + */ update = make_image(im.w, im.h, im.c); - } int e; @@ -266,14 +376,11 @@ void run_nightmare(int argc, char **argv) if(reconstruct){ reconstruct_picture(net, features, im, update, rate, momentum, lambda, smooth_size, 1); //if ((n+1)%30 == 0) rate *= .5; - show_image(im, "reconstruction"); -#ifdef OPENCV - cvWaitKey(10); -#endif + show_image(im, "reconstruction", 10); }else{ int layer = max_layer + rand()%range - range/2; int octave = rand()%octaves; - optimize_picture(&net, im, layer, 1/pow(1.33333333, octave), rate, thresh, norm); + optimize_picture(net, im, layer, 1/pow(1.33333333, octave), rate, thresh, norm); } } fprintf(stderr, "done\n"); @@ -290,8 +397,7 @@ void run_nightmare(int argc, char **argv) } printf("%d %s\n", e, buff); save_image(im, buff); - //show_image(im, buff); - //cvWaitKey(0); + //show_image(im, buff, 0); if(rotate){ image rot = rotate_image(im, rotate); diff --git a/image.darknet/inst/include/darknet/examples/regressor.c b/image.darknet/inst/include/darknet/examples/regressor.c new file mode 100644 index 0000000..20cec0f --- /dev/null +++ b/image.darknet/inst/include/darknet/examples/regressor.c @@ -0,0 +1,240 @@ +#include "darknet.h" +#include +#include + +void train_regressor(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear) +{ + int i; + + float avg_loss = -1; + char *base = basecfg(cfgfile); + printf("%s\n", base); + printf("%d\n", ngpus); + network **nets = calloc(ngpus, sizeof(network*)); + + srand(time(0)); + int seed = rand(); + for(i = 0; i < ngpus; ++i){ + srand(seed); +#ifdef GPU + cuda_set_device(gpus[i]); +#endif + nets[i] = load_network(cfgfile, weightfile, clear); + nets[i]->learning_rate *= ngpus; + } + srand(time(0)); + network *net = nets[0]; + + int imgs = net->batch * net->subdivisions * ngpus; + + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + list *options = read_data_cfg(datacfg); + + char *backup_directory = option_find_str(options, "backup", "/backup/"); + char *train_list = option_find_str(options, "train", "data/train.list"); + int classes = option_find_int(options, "classes", 1); + + list *plist = get_paths(train_list); + char **paths = (char **)list_to_array(plist); + printf("%d\n", plist->size); + int N = plist->size; + clock_t time; + + load_args args = {0}; + args.w = net->w; + args.h = net->h; + args.threads = 32; + args.classes = classes; + + args.min = net->min_ratio*net->w; + args.max = net->max_ratio*net->w; + args.angle = net->angle; + args.aspect = net->aspect; + args.exposure = net->exposure; + args.saturation = net->saturation; + args.hue = net->hue; + args.size = net->w; + + args.paths = paths; + args.n = imgs; + args.m = N; + args.type = REGRESSION_DATA; + + data train; + data buffer; + pthread_t load_thread; + args.d = &buffer; + load_thread = load_data(args); + + int epoch = (*net->seen)/N; + while(get_current_batch(net) < net->max_batches || net->max_batches == 0){ + time=clock(); + + pthread_join(load_thread, 0); + train = buffer; + load_thread = load_data(args); + + printf("Loaded: %lf seconds\n", sec(clock()-time)); + time=clock(); + + float loss = 0; +#ifdef GPU + if(ngpus == 1){ + loss = train_network(net, train); + } else { + loss = train_networks(nets, ngpus, train, 4); + } +#else + loss = train_network(net, train); +#endif + if(avg_loss == -1) avg_loss = loss; + avg_loss = avg_loss*.9 + loss*.1; + printf("%ld, %.3f: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net->seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net->seen); + free_data(train); + if(*net->seen/N > epoch){ + epoch = *net->seen/N; + char buff[256]; + sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); + save_weights(net, buff); + } + if(get_current_batch(net)%100 == 0){ + char buff[256]; + sprintf(buff, "%s/%s.backup",backup_directory,base); + save_weights(net, buff); + } + } + char buff[256]; + sprintf(buff, "%s/%s.weights", backup_directory, base); + save_weights(net, buff); + + free_network(net); + free_ptrs((void**)paths, plist->size); + free_list(plist); + free(base); +} + +void predict_regressor(char *cfgfile, char *weightfile, char *filename) +{ + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + srand(2222222); + + clock_t time; + char buff[256]; + char *input = buff; + 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, "\n"); + } + image im = load_image_color(input, 0, 0); + image sized = letterbox_image(im, net->w, net->h); + + float *X = sized.data; + time=clock(); + float *predictions = network_predict(net, X); + printf("Predicted: %f\n", predictions[0]); + printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); + free_image(im); + free_image(sized); + if (filename) break; + } +} + + +void demo_regressor(char *datacfg, char *cfgfile, char *weightfile, int cam_index, const char *filename) +{ +#ifdef OPENCV + printf("Regressor Demo\n"); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + + srand(2222222); + list *options = read_data_cfg(datacfg); + int classes = option_find_int(options, "classes", 1); + char *name_list = option_find_str(options, "names", 0); + char **names = get_labels(name_list); + + void * cap = open_video_stream(filename, cam_index, 0,0,0); + if(!cap) error("Couldn't connect to webcam.\n"); + float fps = 0; + + while(1){ + struct timeval tval_before, tval_after, tval_result; + gettimeofday(&tval_before, NULL); + + image in = get_image_from_stream(cap); + image crop = center_crop_image(in, net->w, net->h); + grayscale_image_3c(crop); + + float *predictions = network_predict(net, crop.data); + + printf("\033[2J"); + printf("\033[1;1H"); + printf("\nFPS:%.0f\n",fps); + + int i; + for(i = 0; i < classes; ++i){ + printf("%s: %f\n", names[i], predictions[i]); + } + + show_image(crop, "Regressor", 10); + free_image(in); + free_image(crop); + + gettimeofday(&tval_after, NULL); + timersub(&tval_after, &tval_before, &tval_result); + float curr = 1000000.f/((long int)tval_result.tv_usec); + fps = .9*fps + .1*curr; + } +#endif +} + + +void run_regressor(int argc, char **argv) +{ + if(argc < 4){ + fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); + return; + } + + char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); + int *gpus = 0; + int gpu = 0; + int ngpus = 0; + if(gpu_list){ + printf("%s\n", gpu_list); + int len = strlen(gpu_list); + ngpus = 1; + int i; + for(i = 0; i < len; ++i){ + if (gpu_list[i] == ',') ++ngpus; + } + gpus = calloc(ngpus, sizeof(int)); + for(i = 0; i < ngpus; ++i){ + gpus[i] = atoi(gpu_list); + gpu_list = strchr(gpu_list, ',')+1; + } + } else { + gpu = gpu_index; + gpus = &gpu; + ngpus = 1; + } + + int cam_index = find_int_arg(argc, argv, "-c", 0); + int clear = find_arg(argc, argv, "-clear"); + char *data = argv[3]; + char *cfg = argv[4]; + char *weights = (argc > 5) ? argv[5] : 0; + char *filename = (argc > 6) ? argv[6]: 0; + if(0==strcmp(argv[2], "test")) predict_regressor(data, cfg, weights); + else if(0==strcmp(argv[2], "train")) train_regressor(data, cfg, weights, gpus, ngpus, clear); + else if(0==strcmp(argv[2], "demo")) demo_regressor(data, cfg, weights, cam_index, filename); +} + + diff --git a/image.darknet/inst/include/darknet/src/rnn.c b/image.darknet/inst/include/darknet/examples/rnn.c similarity index 72% rename from image.darknet/inst/include/darknet/src/rnn.c rename to image.darknet/inst/include/darknet/examples/rnn.c index eca6f55..5d49eaa 100644 --- a/image.darknet/inst/include/darknet/src/rnn.c +++ b/image.darknet/inst/include/darknet/examples/rnn.c @@ -1,18 +1,26 @@ -#include "network.h" -#include "cost_layer.h" -#include "utils.h" -#include "blas.h" -#include "parser.h" +#include "darknet.h" -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif +#include typedef struct { float *x; float *y; } float_pair; +unsigned char **load_files(char *filename, int *n) +{ + list *paths = get_paths(filename); + *n = paths->size; + unsigned char **contents = calloc(*n, sizeof(char *)); + int i; + node *x = paths->front; + for(i = 0; i < *n; ++i){ + contents[i] = read_file((char *)x->val); + x = x->next; + } + return contents; +} + int *read_tokenized_data(char *filename, size_t *read) { size_t size = 512; @@ -49,6 +57,7 @@ char **read_tokens(char *filename, size_t *read) size = size*2; d = realloc(d, size*sizeof(char *)); } + if(0==strcmp(line, "")) line = "\n"; d[count-1] = line; } fclose(fp); @@ -57,6 +66,7 @@ char **read_tokens(char *filename, size_t *read) return d; } + float_pair get_rnn_token_data(int *tokens, size_t *offsets, int characters, size_t len, int batch, int steps) { float *x = calloc(batch * steps * characters, sizeof(float)); @@ -83,6 +93,37 @@ float_pair get_rnn_token_data(int *tokens, size_t *offsets, int characters, size return p; } +float_pair get_seq2seq_data(char **source, char **dest, int n, int characters, size_t len, int batch, int steps) +{ + int i,j; + float *x = calloc(batch * steps * characters, sizeof(float)); + float *y = calloc(batch * steps * characters, sizeof(float)); + for(i = 0; i < batch; ++i){ + int index = rand()%n; + //int slen = strlen(source[index]); + //int dlen = strlen(dest[index]); + for(j = 0; j < steps; ++j){ + unsigned char curr = source[index][j]; + unsigned char next = dest[index][j]; + + x[(j*batch + i)*characters + curr] = 1; + y[(j*batch + i)*characters + next] = 1; + + if(curr > 255 || curr <= 0 || next > 255 || next <= 0){ + /*text[(index+j+2)%len] = 0; + printf("%ld %d %d %d %d\n", index, j, len, (int)text[index+j], (int)text[index+j+1]); + printf("%s", text+index); + */ + error("Bad char"); + } + } + } + float_pair p; + p.x = x; + p.y = y; + return p; +} + float_pair get_rnn_data(unsigned char *text, size_t *offsets, int characters, size_t len, int batch, int steps) { float *x = calloc(batch * steps * characters, sizeof(float)); @@ -113,19 +154,6 @@ float_pair get_rnn_data(unsigned char *text, size_t *offsets, int characters, si return p; } -void reset_rnn_state(network net, int b) -{ - int i; - for (i = 0; i < net.n; ++i) { - #ifdef GPU - layer l = net.layers[i]; - if(l.state_gpu){ - fill_ongpu(l.outputs, 0, l.state_gpu + l.outputs*b, 1); - } - #endif - } -} - void train_char_rnn(char *cfgfile, char *weightfile, char *filename, int clear, int tokenized) { srand(time(0)); @@ -135,32 +163,22 @@ void train_char_rnn(char *cfgfile, char *weightfile, char *filename, int clear, if(tokenized){ tokens = read_tokenized_data(filename, &size); } else { - FILE *fp = fopen(filename, "rb"); - - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - text = calloc(size+1, sizeof(char)); - fread(text, 1, size, fp); - fclose(fp); + text = read_file(filename); + size = strlen((const char*)text); } char *backup_directory = "/home/pjreddie/backup/"; char *base = basecfg(cfgfile); fprintf(stderr, "%s\n", base); float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, clear); - int inputs = get_network_input_size(net); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int batch = net.batch; - int steps = net.time_steps; - if(clear) *net.seen = 0; - int i = (*net.seen)/net.batch; + int inputs = net->inputs; + fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g, Inputs: %d %d %d\n", net->learning_rate, net->momentum, net->decay, inputs, net->batch, net->time_steps); + int batch = net->batch; + int steps = net->time_steps; + if(clear) *net->seen = 0; + int i = (*net->seen)/net->batch; int streams = batch/steps; size_t *offsets = calloc(streams, sizeof(size_t)); @@ -170,7 +188,7 @@ void train_char_rnn(char *cfgfile, char *weightfile, char *filename, int clear, } clock_t time; - while(get_current_batch(net) < net.max_batches){ + while(get_current_batch(net) < net->max_batches){ i += 1; time=clock(); float_pair p; @@ -180,30 +198,32 @@ void train_char_rnn(char *cfgfile, char *weightfile, char *filename, int clear, p = get_rnn_data(text, offsets, inputs, size, streams, steps); } - float loss = train_network_datum(net, p.x, p.y) / (batch); + copy_cpu(net->inputs*net->batch, p.x, 1, net->input, 1); + copy_cpu(net->truths*net->batch, p.y, 1, net->truth, 1); + float loss = train_network_datum(net) / (batch); free(p.x); free(p.y); if (avg_loss < 0) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - int chars = get_current_batch(net)*batch; + size_t chars = get_current_batch(net)*batch; fprintf(stderr, "%d: %f, %f avg, %f rate, %lf seconds, %f epochs\n", i, loss, avg_loss, get_current_rate(net), sec(clock()-time), (float) chars/size); for(j = 0; j < streams; ++j){ //printf("%d\n", j); - if(rand()%10 == 0){ + if(rand()%64 == 0){ //fprintf(stderr, "Reset\n"); offsets[j] = rand_size_t()%size; - reset_rnn_state(net, j); + reset_network_state(net, j); } } - if(i%1000==0){ + if(i%10000==0){ char buff[256]; sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); save_weights(net, buff); } - if(i%10==0){ + if(i%100==0){ char buff[256]; sprintf(buff, "%s/%s.backup", backup_directory, base); save_weights(net, buff); @@ -234,14 +254,11 @@ void test_char_rnn(char *cfgfile, char *weightfile, int num, char *seed, float t char *base = basecfg(cfgfile); fprintf(stderr, "%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int inputs = get_network_input_size(net); + network *net = load_network(cfgfile, weightfile, 0); + int inputs = net->inputs; int i, j; - for(i = 0; i < net.n; ++i) net.layers[i].temperature = temp; + for(i = 0; i < net->n; ++i) net->layers[i].temperature = temp; int c = 0; int len = strlen(seed); float *input = calloc(inputs, sizeof(float)); @@ -279,7 +296,7 @@ void test_char_rnn(char *cfgfile, char *weightfile, int num, char *seed, float t printf("\n"); } -void test_tactic_rnn(char *cfgfile, char *weightfile, int num, float temp, int rseed, char *token_file) +void test_tactic_rnn_multi(char *cfgfile, char *weightfile, int num, float temp, int rseed, char *token_file) { char **tokens = 0; if(token_file){ @@ -291,14 +308,56 @@ void test_tactic_rnn(char *cfgfile, char *weightfile, int num, float temp, int r char *base = basecfg(cfgfile); fprintf(stderr, "%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); + network *net = load_network(cfgfile, weightfile, 0); + int inputs = net->inputs; + + int i, j; + for(i = 0; i < net->n; ++i) net->layers[i].temperature = temp; + int c = 0; + float *input = calloc(inputs, sizeof(float)); + float *out = 0; + + while(1){ + reset_network_state(net, 0); + while((c = getc(stdin)) != EOF && c != 0){ + input[c] = 1; + out = network_predict(net, input); + input[c] = 0; + } + for(i = 0; i < num; ++i){ + for(j = 0; j < inputs; ++j){ + if (out[j] < .0001) out[j] = 0; + } + int next = sample_array(out, inputs); + if(c == '.' && next == '\n') break; + c = next; + print_symbol(c, tokens); + + input[c] = 1; + out = network_predict(net, input); + input[c] = 0; + } + printf("\n"); + } +} + +void test_tactic_rnn(char *cfgfile, char *weightfile, int num, float temp, int rseed, char *token_file) +{ + char **tokens = 0; + if(token_file){ + size_t n; + tokens = read_tokens(token_file, &n); } - int inputs = get_network_input_size(net); + + srand(rseed); + char *base = basecfg(cfgfile); + fprintf(stderr, "%s\n", base); + + network *net = load_network(cfgfile, weightfile, 0); + int inputs = net->inputs; int i, j; - for(i = 0; i < net.n; ++i) net.layers[i].temperature = temp; + for(i = 0; i < net->n; ++i) net->layers[i].temperature = temp; int c = 0; float *input = calloc(inputs, sizeof(float)); float *out = 0; @@ -329,11 +388,8 @@ void valid_tactic_rnn(char *cfgfile, char *weightfile, char *seed) char *base = basecfg(cfgfile); fprintf(stderr, "%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int inputs = get_network_input_size(net); + network *net = load_network(cfgfile, weightfile, 0); + int inputs = net->inputs; int count = 0; int words = 1; @@ -381,11 +437,8 @@ void valid_char_rnn(char *cfgfile, char *weightfile, char *seed) char *base = basecfg(cfgfile); fprintf(stderr, "%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int inputs = get_network_input_size(net); + network *net = load_network(cfgfile, weightfile, 0); + int inputs = net->inputs; int count = 0; int words = 1; @@ -413,7 +466,7 @@ void valid_char_rnn(char *cfgfile, char *weightfile, char *seed) input[c] = 0; sum += log(out[next])/log2; c = next; - printf("%d Perplexity: %4.4f Word Perplexity: %4.4f\n", count, pow(2, -sum/count), pow(2, -sum/words)); + printf("%d BPC: %4.4f Perplexity: %4.4f Word Perplexity: %4.4f\n", count, -sum/count, pow(2, -sum/count), pow(2, -sum/words)); } } @@ -422,11 +475,8 @@ void vec_char_rnn(char *cfgfile, char *weightfile, char *seed) char *base = basecfg(cfgfile); fprintf(stderr, "%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - int inputs = get_network_input_size(net); + network *net = load_network(cfgfile, weightfile, 0); + int inputs = net->inputs; int c; int seed_len = strlen(seed); @@ -434,7 +484,7 @@ void vec_char_rnn(char *cfgfile, char *weightfile, char *seed) int i; char *line; while((line=fgetl(stdin)) != 0){ - reset_rnn_state(net, 0); + reset_network_state(net, 0); for(i = 0; i < seed_len; ++i){ c = seed[i]; input[(int)c] = 1; @@ -454,7 +504,7 @@ void vec_char_rnn(char *cfgfile, char *weightfile, char *seed) network_predict(net, input); input[(int)c] = 0; - layer l = net.layers[0]; + layer l = net->layers[0]; #ifdef GPU cuda_pull_array(l.output_gpu, l.output, l.outputs); #endif diff --git a/image.darknet/inst/include/darknet/src/rnn_vid.c b/image.darknet/inst/include/darknet/examples/rnn_vid.c similarity index 96% rename from image.darknet/inst/include/darknet/src/rnn_vid.c rename to image.darknet/inst/include/darknet/examples/rnn_vid.c index 36912d6..e887923 100644 --- a/image.darknet/inst/include/darknet/src/rnn_vid.c +++ b/image.darknet/inst/include/darknet/examples/rnn_vid.c @@ -1,11 +1,6 @@ -#include "network.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "blas.h" +#include "darknet.h" #ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" image get_image_from_stream(CvCapture *cap); image ipl_to_image(IplImage* src); @@ -104,7 +99,9 @@ void train_vid_rnn(char *cfgfile, char *weightfile) time=clock(); float_pair p = get_rnn_vid_data(extractor, paths, N, batch, steps); - float loss = train_network_datum(net, p.x, p.y) / (net.batch); + copy_cpu(net.inputs*net.batch, p.x, 1, net.input, 1); + copy_cpu(net.truths*net.batch, p.y, 1, net.truth, 1); + float loss = train_network_datum(net) / (net.batch); free(p.x); diff --git a/image.darknet/inst/include/darknet/examples/segmenter.c b/image.darknet/inst/include/darknet/examples/segmenter.c new file mode 100644 index 0000000..2e7cea0 --- /dev/null +++ b/image.darknet/inst/include/darknet/examples/segmenter.c @@ -0,0 +1,255 @@ +#include "darknet.h" +#include +#include + +void train_segmenter(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear, int display) +{ + int i; + + float avg_loss = -1; + char *base = basecfg(cfgfile); + printf("%s\n", base); + printf("%d\n", ngpus); + network **nets = calloc(ngpus, sizeof(network*)); + + srand(time(0)); + int seed = rand(); + for(i = 0; i < ngpus; ++i){ + srand(seed); +#ifdef GPU + cuda_set_device(gpus[i]); +#endif + nets[i] = load_network(cfgfile, weightfile, clear); + nets[i]->learning_rate *= ngpus; + } + srand(time(0)); + network *net = nets[0]; + image pred = get_network_image(net); + + int div = net->w/pred.w; + assert(pred.w * div == net->w); + assert(pred.h * div == net->h); + + int imgs = net->batch * net->subdivisions * ngpus; + + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + list *options = read_data_cfg(datacfg); + + char *backup_directory = option_find_str(options, "backup", "/backup/"); + char *train_list = option_find_str(options, "train", "data/train.list"); + + list *plist = get_paths(train_list); + char **paths = (char **)list_to_array(plist); + printf("%d\n", plist->size); + int N = plist->size; + + load_args args = {0}; + args.w = net->w; + args.h = net->h; + args.threads = 32; + args.scale = div; + + args.min = net->min_crop; + args.max = net->max_crop; + args.angle = net->angle; + args.aspect = net->aspect; + args.exposure = net->exposure; + args.saturation = net->saturation; + args.hue = net->hue; + args.size = net->w; + args.classes = 80; + + args.paths = paths; + args.n = imgs; + args.m = N; + args.type = SEGMENTATION_DATA; + + data train; + data buffer; + pthread_t load_thread; + args.d = &buffer; + load_thread = load_data(args); + + int epoch = (*net->seen)/N; + while(get_current_batch(net) < net->max_batches || net->max_batches == 0){ + double time = what_time_is_it_now(); + + pthread_join(load_thread, 0); + train = buffer; + load_thread = load_data(args); + + printf("Loaded: %lf seconds\n", what_time_is_it_now()-time); + time = what_time_is_it_now(); + + float loss = 0; +#ifdef GPU + if(ngpus == 1){ + loss = train_network(net, train); + } else { + loss = train_networks(nets, ngpus, train, 4); + } +#else + loss = train_network(net, train); +#endif + if(display){ + image tr = float_to_image(net->w/div, net->h/div, 80, train.y.vals[net->batch*(net->subdivisions-1)]); + image im = float_to_image(net->w, net->h, net->c, train.X.vals[net->batch*(net->subdivisions-1)]); + image mask = mask_to_rgb(tr); + image prmask = mask_to_rgb(pred); + show_image(im, "input", 1); + show_image(prmask, "pred", 1); + show_image(mask, "truth", 100); + free_image(mask); + free_image(prmask); + } + if(avg_loss == -1) avg_loss = loss; + avg_loss = avg_loss*.9 + loss*.1; + printf("%ld, %.3f: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net->seen)/N, loss, avg_loss, get_current_rate(net), what_time_is_it_now()-time, *net->seen); + free_data(train); + if(*net->seen/N > epoch){ + epoch = *net->seen/N; + char buff[256]; + sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); + save_weights(net, buff); + } + if(get_current_batch(net)%100 == 0){ + char buff[256]; + sprintf(buff, "%s/%s.backup",backup_directory,base); + save_weights(net, buff); + } + } + char buff[256]; + sprintf(buff, "%s/%s.weights", backup_directory, base); + save_weights(net, buff); + + free_network(net); + free_ptrs((void**)paths, plist->size); + free_list(plist); + free(base); +} + +void predict_segmenter(char *datafile, char *cfg, char *weights, char *filename) +{ + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); + srand(2222222); + + clock_t time; + char buff[256]; + char *input = buff; + 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, "\n"); + } + image im = load_image_color(input, 0, 0); + image sized = letterbox_image(im, net->w, net->h); + + float *X = sized.data; + time=clock(); + float *predictions = network_predict(net, X); + image pred = get_network_image(net); + image prmask = mask_to_rgb(pred); + printf("Predicted: %f\n", predictions[0]); + printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); + show_image(sized, "orig", 1); + show_image(prmask, "pred", 0); + free_image(im); + free_image(sized); + free_image(prmask); + if (filename) break; + } +} + + +void demo_segmenter(char *datacfg, char *cfg, char *weights, int cam_index, const char *filename) +{ +#ifdef OPENCV + printf("Classifier Demo\n"); + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); + + srand(2222222); + void * cap = open_video_stream(filename, cam_index, 0,0,0); + + if(!cap) error("Couldn't connect to webcam.\n"); + float fps = 0; + + while(1){ + struct timeval tval_before, tval_after, tval_result; + gettimeofday(&tval_before, NULL); + + image in = get_image_from_stream(cap); + image in_s = letterbox_image(in, net->w, net->h); + + network_predict(net, in_s.data); + + printf("\033[2J"); + printf("\033[1;1H"); + printf("\nFPS:%.0f\n",fps); + + image pred = get_network_image(net); + image prmask = mask_to_rgb(pred); + show_image(prmask, "Segmenter", 10); + + free_image(in_s); + free_image(in); + free_image(prmask); + + gettimeofday(&tval_after, NULL); + timersub(&tval_after, &tval_before, &tval_result); + float curr = 1000000.f/((long int)tval_result.tv_usec); + fps = .9*fps + .1*curr; + } +#endif +} + + +void run_segmenter(int argc, char **argv) +{ + if(argc < 4){ + fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); + return; + } + + char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); + int *gpus = 0; + int gpu = 0; + int ngpus = 0; + if(gpu_list){ + printf("%s\n", gpu_list); + int len = strlen(gpu_list); + ngpus = 1; + int i; + for(i = 0; i < len; ++i){ + if (gpu_list[i] == ',') ++ngpus; + } + gpus = calloc(ngpus, sizeof(int)); + for(i = 0; i < ngpus; ++i){ + gpus[i] = atoi(gpu_list); + gpu_list = strchr(gpu_list, ',')+1; + } + } else { + gpu = gpu_index; + gpus = &gpu; + ngpus = 1; + } + + int cam_index = find_int_arg(argc, argv, "-c", 0); + int clear = find_arg(argc, argv, "-clear"); + int display = find_arg(argc, argv, "-display"); + char *data = argv[3]; + char *cfg = argv[4]; + char *weights = (argc > 5) ? argv[5] : 0; + char *filename = (argc > 6) ? argv[6]: 0; + if(0==strcmp(argv[2], "test")) predict_segmenter(data, cfg, weights, filename); + else if(0==strcmp(argv[2], "train")) train_segmenter(data, cfg, weights, gpus, ngpus, clear, display); + else if(0==strcmp(argv[2], "demo")) demo_segmenter(data, cfg, weights, cam_index, filename); +} + + diff --git a/image.darknet/inst/include/darknet/src/super.c b/image.darknet/inst/include/darknet/examples/super.c similarity index 78% rename from image.darknet/inst/include/darknet/src/super.c rename to image.darknet/inst/include/darknet/examples/super.c index 63e9860..d34406b 100644 --- a/image.darknet/inst/include/darknet/src/super.c +++ b/image.darknet/inst/include/darknet/examples/super.c @@ -1,13 +1,6 @@ -#include "network.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" +#include "darknet.h" -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif - -void train_super(char *cfgfile, char *weightfile) +void train_super(char *cfgfile, char *weightfile, int clear) { char *train_images = "/data/imagenet/imagenet1k.train.list"; char *backup_directory = "/home/pjreddie/backup/"; @@ -15,13 +8,10 @@ void train_super(char *cfgfile, char *weightfile) char *base = basecfg(cfgfile); printf("%s\n", base); float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - int i = *net.seen/imgs; + network *net = load_network(cfgfile, weightfile, clear); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + int imgs = net->batch*net->subdivisions; + int i = *net->seen/imgs; data train, buffer; @@ -30,8 +20,8 @@ void train_super(char *cfgfile, char *weightfile) char **paths = (char **)list_to_array(plist); load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; args.scale = 4; args.paths = paths; args.n = imgs; @@ -42,7 +32,7 @@ void train_super(char *cfgfile, char *weightfile) pthread_t load_thread = load_data_in_thread(args); clock_t time; //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ + while(get_current_batch(net) < net->max_batches){ i += 1; time=clock(); pthread_join(load_thread, 0); @@ -76,11 +66,8 @@ void train_super(char *cfgfile, char *weightfile) void test_super(char *cfgfile, char *weightfile, char *filename) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); srand(2222222); clock_t time; @@ -97,7 +84,7 @@ void test_super(char *cfgfile, char *weightfile, char *filename) strtok(input, "\n"); } image im = load_image_color(input, 0, 0); - resize_network(&net, im.w, im.h); + resize_network(net, im.w, im.h); printf("%d %d\n", im.w, im.h); float *X = im.data; @@ -106,6 +93,7 @@ void test_super(char *cfgfile, char *weightfile, char *filename) image out = get_network_image(net); printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); save_image(out, "out"); + show_image(out, "out", 0); free_image(im); if (filename) break; @@ -123,7 +111,8 @@ void run_super(int argc, char **argv) char *cfg = argv[3]; char *weights = (argc > 4) ? argv[4] : 0; char *filename = (argc > 5) ? argv[5] : 0; - if(0==strcmp(argv[2], "train")) train_super(cfg, weights); + int clear = find_arg(argc, argv, "-clear"); + if(0==strcmp(argv[2], "train")) train_super(cfg, weights, clear); else if(0==strcmp(argv[2], "test")) test_super(cfg, weights, filename); /* else if(0==strcmp(argv[2], "valid")) validate_super(cfg, weights); diff --git a/image.darknet/inst/include/darknet/src/swag.c b/image.darknet/inst/include/darknet/examples/swag.c similarity index 92% rename from image.darknet/inst/include/darknet/src/swag.c rename to image.darknet/inst/include/darknet/examples/swag.c index 2cb3093..c22d785 100644 --- a/image.darknet/inst/include/darknet/src/swag.c +++ b/image.darknet/inst/include/darknet/examples/swag.c @@ -1,13 +1,5 @@ -#include "network.h" -#include "detection_layer.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "box.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif +#include "darknet.h" +#include void train_swag(char *cfgfile, char *weightfile) { diff --git a/image.darknet/inst/include/darknet/src/tag.c b/image.darknet/inst/include/darknet/examples/tag.c similarity index 72% rename from image.darknet/inst/include/darknet/src/tag.c rename to image.darknet/inst/include/darknet/examples/tag.c index 1e43e7d..4caf8cb 100644 --- a/image.darknet/inst/include/darknet/src/tag.c +++ b/image.darknet/inst/include/darknet/examples/tag.c @@ -1,10 +1,4 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif +#include "darknet.h" void train_tag(char *cfgfile, char *weightfile, int clear) { @@ -13,12 +7,8 @@ void train_tag(char *cfgfile, char *weightfile, int clear) char *base = basecfg(cfgfile); char *backup_directory = "/home/pjreddie/backup/"; printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - if(clear) *net.seen = 0; - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + network *net = load_network(cfgfile, weightfile, clear); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); int imgs = 1024; list *plist = get_paths("/home/pjreddie/tag/train.list"); char **paths = (char **)list_to_array(plist); @@ -30,30 +20,30 @@ void train_tag(char *cfgfile, char *weightfile, int clear) data buffer; load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; - args.min = net.w; - args.max = net.max_crop; - args.size = net.w; + args.min = net->w; + args.max = net->max_crop; + args.size = net->w; args.paths = paths; - args.classes = net.outputs; + args.classes = net->outputs; args.n = imgs; args.m = N; args.d = &buffer; args.type = TAG_DATA; - args.angle = net.angle; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; + args.angle = net->angle; + args.exposure = net->exposure; + args.saturation = net->saturation; + args.hue = net->hue; - fprintf(stderr, "%d classes\n", net.outputs); + fprintf(stderr, "%d classes\n", net->outputs); load_thread = load_data_in_thread(args); - int epoch = (*net.seen)/N; - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ + int epoch = (*net->seen)/N; + while(get_current_batch(net) < net->max_batches || net->max_batches == 0){ time=clock(); pthread_join(load_thread, 0); train = buffer; @@ -64,10 +54,10 @@ void train_tag(char *cfgfile, char *weightfile, int clear) float loss = train_network(net, train); if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); + printf("%ld, %.3f: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net->seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net->seen); free_data(train); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; + if(*net->seen/N > epoch){ + epoch = *net->seen/N; char buff[256]; sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); save_weights(net, buff); @@ -92,11 +82,8 @@ void train_tag(char *cfgfile, char *weightfile, int clear) void test_tag(char *cfgfile, char *weightfile, char *filename) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); srand(2222222); int i = 0; char **names = get_labels("data/tags.txt"); @@ -104,7 +91,7 @@ void test_tag(char *cfgfile, char *weightfile, char *filename) int indexes[10]; char buff[256]; char *input = buff; - int size = net.w; + int size = net->w; while(1){ if(filename){ strncpy(input, filename, 256); @@ -117,7 +104,7 @@ void test_tag(char *cfgfile, char *weightfile, char *filename) } image im = load_image_color(input, 0, 0); image r = resize_min(im, size); - resize_network(&net, r.w, r.h); + resize_network(net, r.w, r.h); printf("%d %d\n", r.w, r.h); float *X = r.data; diff --git a/image.darknet/inst/include/darknet/src/voxel.c b/image.darknet/inst/include/darknet/examples/voxel.c similarity index 96% rename from image.darknet/inst/include/darknet/src/voxel.c rename to image.darknet/inst/include/darknet/examples/voxel.c index 1b53880..01ea9bb 100644 --- a/image.darknet/inst/include/darknet/src/voxel.c +++ b/image.darknet/inst/include/darknet/examples/voxel.c @@ -1,12 +1,4 @@ -#include "network.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -image get_image_from_stream(CvCapture *cap); -#endif +#include "darknet.h" void extract_voxel(char *lfile, char *rfile, char *prefix) { diff --git a/image.darknet/inst/include/darknet/src/writing.c b/image.darknet/inst/include/darknet/examples/writing.c similarity index 90% rename from image.darknet/inst/include/darknet/src/writing.c rename to image.darknet/inst/include/darknet/examples/writing.c index 0a76d48..1b6ff83 100644 --- a/image.darknet/inst/include/darknet/src/writing.c +++ b/image.darknet/inst/include/darknet/examples/writing.c @@ -1,10 +1,4 @@ -#include "network.h" -#include "utils.h" -#include "parser.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif +#include "darknet.h" void train_writing(char *cfgfile, char *weightfile) { @@ -69,11 +63,11 @@ void train_writing(char *cfgfile, char *weightfile) if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); + printf("%ld, %.3f: %f, %f avg, %f rate, %lf seconds, %ld images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); free_data(train); if(get_current_batch(net)%100 == 0){ char buff[256]; - sprintf(buff, "%s/%s_batch_%d.weights", backup_directory, base, get_current_batch(net)); + sprintf(buff, "%s/%s_batch_%ld.weights", backup_directory, base, get_current_batch(net)); save_weights(net, buff); } if(*net.seen/N > epoch){ diff --git a/image.darknet/inst/include/darknet/src/yolo.c b/image.darknet/inst/include/darknet/examples/yolo.c similarity index 68% rename from image.darknet/inst/include/darknet/src/yolo.c rename to image.darknet/inst/include/darknet/examples/yolo.c index ee5f73b..4ddb69a 100644 --- a/image.darknet/inst/include/darknet/src/yolo.c +++ b/image.darknet/inst/include/darknet/examples/yolo.c @@ -1,14 +1,4 @@ -#include "network.h" -#include "detection_layer.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "box.h" -#include "demo.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif +#include "darknet.h" char *voc_names[] = {"aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"}; @@ -20,17 +10,14 @@ void train_yolo(char *cfgfile, char *weightfile) char *base = basecfg(cfgfile); printf("%s\n", base); float avg_loss = -1; - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - int imgs = net.batch*net.subdivisions; - int i = *net.seen/imgs; + network *net = load_network(cfgfile, weightfile, 0); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); + int imgs = net->batch*net->subdivisions; + int i = *net->seen/imgs; data train, buffer; - layer l = net.layers[net.n - 1]; + layer l = net->layers[net->n - 1]; int side = l.side; int classes = l.classes; @@ -41,8 +28,8 @@ void train_yolo(char *cfgfile, char *weightfile) char **paths = (char **)list_to_array(plist); load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; args.paths = paths; args.n = imgs; args.m = plist->size; @@ -52,15 +39,15 @@ void train_yolo(char *cfgfile, char *weightfile) args.d = &buffer; args.type = REGION_DATA; - args.angle = net.angle; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; + args.angle = net->angle; + args.exposure = net->exposure; + args.saturation = net->saturation; + args.hue = net->hue; pthread_t load_thread = load_data_in_thread(args); clock_t time; //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ + while(get_current_batch(net) < net->max_batches){ i += 1; time=clock(); pthread_join(load_thread, 0); @@ -87,14 +74,14 @@ void train_yolo(char *cfgfile, char *weightfile) save_weights(net, buff); } -void print_yolo_detections(FILE **fps, char *id, box *boxes, float **probs, int total, int classes, int w, int h) +void print_yolo_detections(FILE **fps, char *id, int total, int classes, int w, int h, detection *dets) { int i, j; for(i = 0; i < total; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; + float xmin = dets[i].bbox.x - dets[i].bbox.w/2.; + float xmax = dets[i].bbox.x + dets[i].bbox.w/2.; + float ymin = dets[i].bbox.y - dets[i].bbox.h/2.; + float ymax = dets[i].bbox.y + dets[i].bbox.h/2.; if (xmin < 0) xmin = 0; if (ymin < 0) ymin = 0; @@ -102,20 +89,17 @@ void print_yolo_detections(FILE **fps, char *id, box *boxes, float **probs, int if (ymax > h) ymax = h; for(j = 0; j < classes; ++j){ - if (probs[i][j]) fprintf(fps[j], "%s %f %f %f %f %f\n", id, probs[i][j], + if (dets[i].prob[j]) fprintf(fps[j], "%s %f %f %f %f %f\n", id, dets[i].prob[j], xmin, ymin, xmax, ymax); } } } -void validate_yolo(char *cfgfile, char *weightfile) +void validate_yolo(char *cfg, char *weights) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); + fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); srand(time(0)); char *base = "results/comp4_det_test_"; @@ -124,7 +108,7 @@ void validate_yolo(char *cfgfile, char *weightfile) //list *plist = get_paths("data/voc.2012.test"); char **paths = (char **)list_to_array(plist); - layer l = net.layers[net.n-1]; + layer l = net->layers[net->n-1]; int classes = l.classes; int j; @@ -134,9 +118,6 @@ void validate_yolo(char *cfgfile, char *weightfile) snprintf(buff, 1024, "%s%s.txt", base, voc_names[j]); fps[j] = fopen(buff, "w"); } - box *boxes = calloc(l.side*l.side*l.n, sizeof(box)); - float **probs = calloc(l.side*l.side*l.n, sizeof(float *)); - for(j = 0; j < l.side*l.side*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); int m = plist->size; int i=0; @@ -154,8 +135,8 @@ void validate_yolo(char *cfgfile, char *weightfile) pthread_t *thr = calloc(nthreads, sizeof(pthread_t)); load_args args = {0}; - args.w = net.w; - args.h = net.h; + args.w = net->w; + args.h = net->h; args.type = IMAGE_DATA; for(t = 0; t < nthreads; ++t){ @@ -185,9 +166,11 @@ void validate_yolo(char *cfgfile, char *weightfile) network_predict(net, X); int w = val[t].w; int h = val[t].h; - get_detection_boxes(l, w, h, thresh, probs, boxes, 0); - if (nms) do_nms_sort(boxes, probs, l.side*l.side*l.n, classes, iou_thresh); - print_yolo_detections(fps, id, boxes, probs, l.side*l.side*l.n, classes, w, h); + int nboxes = 0; + detection *dets = get_network_boxes(net, w, h, thresh, 0, 0, 0, &nboxes); + if (nms) do_nms_sort(dets, l.side*l.side*l.n, classes, iou_thresh); + print_yolo_detections(fps, id, l.side*l.side*l.n, classes, w, h, dets); + free_detections(dets, nboxes); free(id); free_image(val[t]); free_image(val_resized[t]); @@ -196,21 +179,18 @@ void validate_yolo(char *cfgfile, char *weightfile) fprintf(stderr, "Total Detection Time: %f Seconds\n", (double)(time(0) - start)); } -void validate_yolo_recall(char *cfgfile, char *weightfile) +void validate_yolo_recall(char *cfg, char *weights) { - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); + network *net = load_network(cfg, weights, 0); + set_batch_network(net, 1); + fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net->learning_rate, net->momentum, net->decay); srand(time(0)); char *base = "results/comp4_det_test_"; list *plist = get_paths("data/voc.2007.test"); char **paths = (char **)list_to_array(plist); - layer l = net.layers[net.n-1]; + layer l = net->layers[net->n-1]; int classes = l.classes; int side = l.side; @@ -221,9 +201,6 @@ void validate_yolo_recall(char *cfgfile, char *weightfile) snprintf(buff, 1024, "%s%s.txt", base, voc_names[j]); fps[j] = fopen(buff, "w"); } - box *boxes = calloc(side*side*l.n, sizeof(box)); - float **probs = calloc(side*side*l.n, sizeof(float *)); - for(j = 0; j < side*side*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); int m = plist->size; int i=0; @@ -240,11 +217,13 @@ void validate_yolo_recall(char *cfgfile, char *weightfile) for(i = 0; i < m; ++i){ char *path = paths[i]; image orig = load_image_color(path, 0, 0); - image sized = resize_image(orig, net.w, net.h); + image sized = resize_image(orig, net->w, net->h); char *id = basecfg(path); network_predict(net, sized.data); - get_detection_boxes(l, orig.w, orig.h, thresh, probs, boxes, 1); - if (nms) do_nms(boxes, probs, side*side*l.n, 1, nms); + + int nboxes = 0; + detection *dets = get_network_boxes(net, orig.w, orig.h, thresh, 0, 0, 1, &nboxes); + if (nms) do_nms_obj(dets, side*side*l.n, 1, nms); char labelpath[4096]; find_replace(path, "images", "labels", labelpath); @@ -255,7 +234,7 @@ void validate_yolo_recall(char *cfgfile, char *weightfile) int num_labels = 0; box_label *truth = read_boxes(labelpath, &num_labels); for(k = 0; k < side*side*l.n; ++k){ - if(probs[k][0] > thresh){ + if(dets[k].objectness > thresh){ ++proposals; } } @@ -264,8 +243,8 @@ void validate_yolo_recall(char *cfgfile, char *weightfile) box t = {truth[j].x, truth[j].y, truth[j].w, truth[j].h}; float best_iou = 0; for(k = 0; k < side*side*l.n; ++k){ - float iou = box_iou(boxes[k], t); - if(probs[k][0] > thresh && iou > best_iou){ + float iou = box_iou(dets[k].bbox, t); + if(dets[k].objectness > thresh && iou > best_iou){ best_iou = iou; } } @@ -276,6 +255,7 @@ void validate_yolo_recall(char *cfgfile, char *weightfile) } fprintf(stderr, "%5d %5d %5d\tRPs/Img: %.2f\tIOU: %.2f%%\tRecall:%.2f%%\n", i, correct, total, (float)proposals/(i+1), avg_iou*100/total, 100.*correct/total); + free_detections(dets, nboxes); free(id); free_image(orig); free_image(sized); @@ -285,21 +265,14 @@ void validate_yolo_recall(char *cfgfile, char *weightfile) void test_yolo(char *cfgfile, char *weightfile, char *filename, float thresh) { image **alphabet = load_alphabet(); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - detection_layer l = net.layers[net.n-1]; - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + layer l = net->layers[net->n-1]; + set_batch_network(net, 1); srand(2222222); clock_t time; char buff[256]; char *input = buff; - int j; float nms=.4; - box *boxes = calloc(l.side*l.side*l.n, sizeof(box)); - float **probs = calloc(l.side*l.side*l.n, sizeof(float *)); - for(j = 0; j < l.side*l.side*l.n; ++j) probs[j] = calloc(l.classes, sizeof(float *)); while(1){ if(filename){ strncpy(input, filename, 256); @@ -311,24 +284,22 @@ void test_yolo(char *cfgfile, char *weightfile, char *filename, float thresh) strtok(input, "\n"); } image im = load_image_color(input,0,0); - image sized = resize_image(im, net.w, net.h); + image sized = resize_image(im, net->w, net->h); float *X = sized.data; time=clock(); network_predict(net, X); printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - get_detection_boxes(l, 1, 1, thresh, probs, boxes, 0); - if (nms) do_nms_sort(boxes, probs, l.side*l.side*l.n, l.classes, nms); - //draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, voc_names, alphabet, 20); - draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, voc_names, alphabet, 20); - save_image(im, "predictions"); - show_image(im, "predictions"); + int nboxes = 0; + detection *dets = get_network_boxes(net, 1, 1, thresh, 0, 0, 0, &nboxes); + if (nms) do_nms_sort(dets, l.side*l.side*l.n, l.classes, nms); + + draw_detections(im, dets, l.side*l.side*l.n, thresh, voc_names, alphabet, 20); + save_image(im, "predictions"); + show_image(im, "predictions", 0); + free_detections(dets, nboxes); free_image(im); free_image(sized); -#ifdef OPENCV - cvWaitKey(0); - cvDestroyAllWindows(); -#endif if (filename) break; } } @@ -344,6 +315,7 @@ void run_yolo(int argc, char **argv) return; } + int avg = find_int_arg(argc, argv, "-avg", 1); char *cfg = argv[3]; char *weights = (argc > 4) ? argv[4] : 0; char *filename = (argc > 5) ? argv[5]: 0; @@ -351,5 +323,5 @@ void run_yolo(int argc, char **argv) else if(0==strcmp(argv[2], "train")) train_yolo(cfg, weights); else if(0==strcmp(argv[2], "valid")) validate_yolo(cfg, weights); else if(0==strcmp(argv[2], "recall")) validate_yolo_recall(cfg, weights); - else if(0==strcmp(argv[2], "demo")) demo(cfg, weights, thresh, cam_index, filename, voc_names, 20, frame_skip, prefix, .5); + else if(0==strcmp(argv[2], "demo")) demo(cfg, weights, thresh, cam_index, filename, voc_names, 20, frame_skip, prefix, avg, .5, 0,0,0,0); } diff --git a/image.darknet/inst/include/darknet/include/darknet.h b/image.darknet/inst/include/darknet/include/darknet.h new file mode 100644 index 0000000..4390c61 --- /dev/null +++ b/image.darknet/inst/include/darknet/include/darknet.h @@ -0,0 +1,805 @@ +#ifndef DARKNET_API +#define DARKNET_API +#include +#include +#include +#include + +#ifdef GPU + #define BLOCK 512 + + #include "cuda_runtime.h" + #include "curand.h" + #include "cublas_v2.h" + + #ifdef CUDNN + #include "cudnn.h" + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define SECRET_NUM -1234 +extern int gpu_index; + +typedef struct{ + int classes; + char **names; +} metadata; + +metadata get_metadata(char *file); + +typedef struct{ + int *leaf; + int n; + int *parent; + int *child; + int *group; + char **name; + + int groups; + int *group_size; + int *group_offset; +} tree; +tree *read_tree(char *filename); + +typedef enum{ + LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU +} ACTIVATION; + +typedef enum{ + PNG, BMP, TGA, JPG +} IMTYPE; + +typedef enum{ + MULT, ADD, SUB, DIV +} BINARY_ACTIVATION; + +typedef enum { + CONVOLUTIONAL, + DECONVOLUTIONAL, + CONNECTED, + MAXPOOL, + SOFTMAX, + DETECTION, + DROPOUT, + CROP, + ROUTE, + COST, + NORMALIZATION, + AVGPOOL, + LOCAL, + SHORTCUT, + ACTIVE, + RNN, + GRU, + LSTM, + CRNN, + BATCHNORM, + NETWORK, + XNOR, + REGION, + YOLO, + ISEG, + REORG, + UPSAMPLE, + LOGXENT, + L2NORM, + BLANK +} LAYER_TYPE; + +typedef enum{ + SSE, MASKED, L1, SEG, SMOOTH,WGAN +} COST_TYPE; + +typedef struct{ + int batch; + float learning_rate; + float momentum; + float decay; + int adam; + float B1; + float B2; + float eps; + int t; +} update_args; + +struct network; +typedef struct network network; + +struct layer; +typedef struct layer layer; + +struct layer{ + LAYER_TYPE type; + ACTIVATION activation; + COST_TYPE cost_type; + void (*forward) (struct layer, struct network); + void (*backward) (struct layer, struct network); + void (*update) (struct layer, update_args); + void (*forward_gpu) (struct layer, struct network); + void (*backward_gpu) (struct layer, struct network); + void (*update_gpu) (struct layer, update_args); + int batch_normalize; + int shortcut; + int batch; + int forced; + int flipped; + int inputs; + int outputs; + int nweights; + int nbiases; + int extra; + int truths; + int h,w,c; + int out_h, out_w, out_c; + int n; + int max_boxes; + int groups; + int size; + int side; + int stride; + int reverse; + int flatten; + int spatial; + int pad; + int sqrt; + int flip; + int index; + int binary; + int xnor; + int steps; + int hidden; + int truth; + float smooth; + float dot; + float angle; + float jitter; + float saturation; + float exposure; + float shift; + float ratio; + float learning_rate_scale; + float clip; + int noloss; + int softmax; + int classes; + int coords; + int background; + int rescore; + int objectness; + int joint; + int noadjust; + int reorg; + int log; + int tanh; + int *mask; + int total; + + float alpha; + float beta; + float kappa; + + float coord_scale; + float object_scale; + float noobject_scale; + float mask_scale; + float class_scale; + int bias_match; + int random; + float ignore_thresh; + float truth_thresh; + float thresh; + float focus; + int classfix; + int absolute; + + int onlyforward; + int stopbackward; + int dontload; + int dontsave; + int dontloadscales; + int numload; + + float temperature; + float probability; + float scale; + + char * cweights; + int * indexes; + int * input_layers; + int * input_sizes; + int * map; + int * counts; + float ** sums; + float * rand; + float * cost; + float * state; + float * prev_state; + float * forgot_state; + float * forgot_delta; + float * state_delta; + float * combine_cpu; + float * combine_delta_cpu; + + float * concat; + float * concat_delta; + + float * binary_weights; + + float * biases; + float * bias_updates; + + float * scales; + float * scale_updates; + + float * weights; + float * weight_updates; + + float * delta; + float * output; + float * loss; + float * squared; + float * norms; + + float * spatial_mean; + float * mean; + float * variance; + + float * mean_delta; + float * variance_delta; + + float * rolling_mean; + float * rolling_variance; + + float * x; + float * x_norm; + + float * m; + float * v; + + float * bias_m; + float * bias_v; + float * scale_m; + float * scale_v; + + + float *z_cpu; + float *r_cpu; + float *h_cpu; + float * prev_state_cpu; + + float *temp_cpu; + float *temp2_cpu; + float *temp3_cpu; + + float *dh_cpu; + float *hh_cpu; + float *prev_cell_cpu; + float *cell_cpu; + float *f_cpu; + float *i_cpu; + float *g_cpu; + float *o_cpu; + float *c_cpu; + float *dc_cpu; + + float * binary_input; + + struct layer *input_layer; + struct layer *self_layer; + struct layer *output_layer; + + struct layer *reset_layer; + struct layer *update_layer; + struct layer *state_layer; + + struct layer *input_gate_layer; + struct layer *state_gate_layer; + struct layer *input_save_layer; + struct layer *state_save_layer; + struct layer *input_state_layer; + struct layer *state_state_layer; + + struct layer *input_z_layer; + struct layer *state_z_layer; + + struct layer *input_r_layer; + struct layer *state_r_layer; + + struct layer *input_h_layer; + struct layer *state_h_layer; + + struct layer *wz; + struct layer *uz; + struct layer *wr; + struct layer *ur; + struct layer *wh; + struct layer *uh; + struct layer *uo; + struct layer *wo; + struct layer *uf; + struct layer *wf; + struct layer *ui; + struct layer *wi; + struct layer *ug; + struct layer *wg; + + tree *softmax_tree; + + size_t workspace_size; + +#ifdef GPU + int *indexes_gpu; + + float *z_gpu; + float *r_gpu; + float *h_gpu; + + float *temp_gpu; + float *temp2_gpu; + float *temp3_gpu; + + float *dh_gpu; + float *hh_gpu; + float *prev_cell_gpu; + float *cell_gpu; + float *f_gpu; + float *i_gpu; + float *g_gpu; + float *o_gpu; + float *c_gpu; + float *dc_gpu; + + float *m_gpu; + float *v_gpu; + float *bias_m_gpu; + float *scale_m_gpu; + float *bias_v_gpu; + float *scale_v_gpu; + + float * combine_gpu; + float * combine_delta_gpu; + + float * prev_state_gpu; + float * forgot_state_gpu; + float * forgot_delta_gpu; + float * state_gpu; + float * state_delta_gpu; + float * gate_gpu; + float * gate_delta_gpu; + float * save_gpu; + float * save_delta_gpu; + float * concat_gpu; + float * concat_delta_gpu; + + float * binary_input_gpu; + float * binary_weights_gpu; + + float * mean_gpu; + float * variance_gpu; + + float * rolling_mean_gpu; + float * rolling_variance_gpu; + + float * variance_delta_gpu; + float * mean_delta_gpu; + + float * x_gpu; + float * x_norm_gpu; + float * weights_gpu; + float * weight_updates_gpu; + float * weight_change_gpu; + + float * biases_gpu; + float * bias_updates_gpu; + float * bias_change_gpu; + + float * scales_gpu; + float * scale_updates_gpu; + float * scale_change_gpu; + + float * output_gpu; + float * loss_gpu; + float * delta_gpu; + float * rand_gpu; + float * squared_gpu; + float * norms_gpu; +#ifdef CUDNN + cudnnTensorDescriptor_t srcTensorDesc, dstTensorDesc; + cudnnTensorDescriptor_t dsrcTensorDesc, ddstTensorDesc; + cudnnTensorDescriptor_t normTensorDesc; + cudnnFilterDescriptor_t weightDesc; + cudnnFilterDescriptor_t dweightDesc; + cudnnConvolutionDescriptor_t convDesc; + cudnnConvolutionFwdAlgo_t fw_algo; + cudnnConvolutionBwdDataAlgo_t bd_algo; + cudnnConvolutionBwdFilterAlgo_t bf_algo; +#endif +#endif +}; + +void free_layer(layer); + +typedef enum { + CONSTANT, STEP, EXP, POLY, STEPS, SIG, RANDOM +} learning_rate_policy; + +typedef struct network{ + int n; + int batch; + size_t *seen; + int *t; + float epoch; + int subdivisions; + layer *layers; + float *output; + learning_rate_policy policy; + + float learning_rate; + float momentum; + float decay; + float gamma; + float scale; + float power; + int time_steps; + int step; + int max_batches; + float *scales; + int *steps; + int num_steps; + int burn_in; + + int adam; + float B1; + float B2; + float eps; + + int inputs; + int outputs; + int truths; + int notruth; + int h, w, c; + int max_crop; + int min_crop; + float max_ratio; + float min_ratio; + int center; + float angle; + float aspect; + float exposure; + float saturation; + float hue; + int random; + + int gpu_index; + tree *hierarchy; + + float *input; + float *truth; + float *delta; + float *workspace; + int train; + int index; + float *cost; + float clip; + +#ifdef GPU + float *input_gpu; + float *truth_gpu; + float *delta_gpu; + float *output_gpu; +#endif + +} network; + +typedef struct { + int w; + int h; + float scale; + float rad; + float dx; + float dy; + float aspect; +} augment_args; + +typedef struct { + int w; + int h; + int c; + float *data; +} image; + +typedef struct{ + float x, y, w, h; +} box; + +typedef struct detection{ + box bbox; + int classes; + float *prob; + float *mask; + float objectness; + int sort_class; +} detection; + +typedef struct matrix{ + int rows, cols; + float **vals; +} matrix; + + +typedef struct{ + int w, h; + matrix X; + matrix y; + int shallow; + int *num_boxes; + box **boxes; +} data; + +typedef enum { + CLASSIFICATION_DATA, DETECTION_DATA, CAPTCHA_DATA, REGION_DATA, IMAGE_DATA, COMPARE_DATA, WRITING_DATA, SWAG_DATA, TAG_DATA, OLD_CLASSIFICATION_DATA, STUDY_DATA, DET_DATA, SUPER_DATA, LETTERBOX_DATA, REGRESSION_DATA, SEGMENTATION_DATA, INSTANCE_DATA, ISEG_DATA +} data_type; + +typedef struct load_args{ + int threads; + char **paths; + char *path; + int n; + int m; + char **labels; + int h; + int w; + int out_w; + int out_h; + int nh; + int nw; + int num_boxes; + int min, max, size; + int classes; + int background; + int scale; + int center; + int coords; + float jitter; + float angle; + float aspect; + float saturation; + float exposure; + float hue; + data *d; + image *im; + image *resized; + data_type type; + tree *hierarchy; +} load_args; + +typedef struct{ + int id; + float x,y,w,h; + float left, right, top, bottom; +} box_label; + + +network *load_network(char *cfg, char *weights, int clear); +load_args get_base_args(network *net); + +void free_data(data d); + +typedef struct node{ + void *val; + struct node *next; + struct node *prev; +} node; + +typedef struct list{ + int size; + node *front; + node *back; +} list; + +pthread_t load_data(load_args args); +list *read_data_cfg(char *filename); +list *read_cfg(char *filename); +unsigned char *read_file(char *filename); +data resize_data(data orig, int w, int h); +data *tile_data(data orig, int divs, int size); +data select_data(data *orig, int *inds); + +void forward_network(network *net); +void backward_network(network *net); +void update_network(network *net); + + +float dot_cpu(int N, float *X, int INCX, float *Y, int INCY); +void axpy_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); +void copy_cpu(int N, float *X, int INCX, float *Y, int INCY); +void scal_cpu(int N, float ALPHA, float *X, int INCX); +void fill_cpu(int N, float ALPHA, float * X, int INCX); +void normalize_cpu(float *x, float *mean, float *variance, int batch, int filters, int spatial); +void softmax(float *input, int n, float temp, int stride, float *output); + +int best_3d_shift_r(image a, image b, int min, int max); +#ifdef GPU +void axpy_gpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY); +void fill_gpu(int N, float ALPHA, float * X, int INCX); +void scal_gpu(int N, float ALPHA, float * X, int INCX); +void copy_gpu(int N, float * X, int INCX, float * Y, int INCY); + +void cuda_set_device(int n); +void cuda_free(float *x_gpu); +float *cuda_make_array(float *x, size_t n); +void cuda_pull_array(float *x_gpu, float *x, size_t n); +float cuda_mag_array(float *x_gpu, size_t n); +void cuda_push_array(float *x_gpu, float *x, size_t n); + +void forward_network_gpu(network *net); +void backward_network_gpu(network *net); +void update_network_gpu(network *net); + +float train_networks(network **nets, int n, data d, int interval); +void sync_nets(network **nets, int n, int interval); +void harmless_update_network_gpu(network *net); +#endif +image get_label(image **characters, char *string, int size); +void draw_label(image a, int r, int c, image label, const float *rgb); +void save_image(image im, const char *name); +void save_image_options(image im, const char *name, IMTYPE f, int quality); +void get_next_batch(data d, int n, int offset, float *X, float *y); +void grayscale_image_3c(image im); +void normalize_image(image p); +void matrix_to_csv(matrix m); +float train_network_sgd(network *net, data d, int n); +void rgbgr_image(image im); +data copy_data(data d); +data concat_data(data d1, data d2); +data load_cifar10_data(char *filename); +float matrix_topk_accuracy(matrix truth, matrix guess, int k); +void matrix_add_matrix(matrix from, matrix to); +void scale_matrix(matrix m, float scale); +matrix csv_to_matrix(char *filename); +float *network_accuracies(network *net, data d, int n); +float train_network_datum(network *net); +image make_random_image(int w, int h, int c); + +void denormalize_connected_layer(layer l); +void denormalize_convolutional_layer(layer l); +void statistics_connected_layer(layer l); +void rescale_weights(layer l, float scale, float trans); +void rgbgr_weights(layer l); +image *get_weights(layer l); + +void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int frame_skip, char *prefix, int avg, float hier_thresh, int w, int h, int fps, int fullscreen); +void get_detection_detections(layer l, int w, int h, float thresh, detection *dets); + +char *option_find_str(list *l, char *key, char *def); +int option_find_int(list *l, char *key, int def); +int option_find_int_quiet(list *l, char *key, int def); + +network *parse_network_cfg(char *filename); +void save_weights(network *net, char *filename); +void load_weights(network *net, char *filename); +void save_weights_upto(network *net, char *filename, int cutoff); +void load_weights_upto(network *net, char *filename, int start, int cutoff); + +void zero_objectness(layer l); +void get_region_detections(layer l, int w, int h, int netw, int neth, float thresh, int *map, float tree_thresh, int relative, detection *dets); +int get_yolo_detections(layer l, int w, int h, int netw, int neth, float thresh, int *map, int relative, detection *dets); +void free_network(network *net); +void set_batch_network(network *net, int b); +void set_temp_network(network *net, float t); +image load_image(char *filename, int w, int h, int c); +image load_image_color(char *filename, int w, int h); +image make_image(int w, int h, int c); +image resize_image(image im, int w, int h); +void censor_image(image im, int dx, int dy, int w, int h); +image letterbox_image(image im, int w, int h); +image crop_image(image im, int dx, int dy, int w, int h); +image center_crop_image(image im, int w, int h); +image resize_min(image im, int min); +image resize_max(image im, int max); +image threshold_image(image im, float thresh); +image mask_to_rgb(image mask); +int resize_network(network *net, int w, int h); +void free_matrix(matrix m); +void test_resize(char *filename); +int show_image(image p, const char *name, int ms); +image copy_image(image p); +void draw_box_width(image a, int x1, int y1, int x2, int y2, int w, float r, float g, float b); +float get_current_rate(network *net); +void composite_3d(char *f1, char *f2, char *out, int delta); +data load_data_old(char **paths, int n, int m, char **labels, int k, int w, int h); +size_t get_current_batch(network *net); +void constrain_image(image im); +image get_network_image_layer(network *net, int i); +layer get_network_output_layer(network *net); +void top_predictions(network *net, int n, int *index); +void flip_image(image a); +image float_to_image(int w, int h, int c, float *data); +void ghost_image(image source, image dest, int dx, int dy); +float network_accuracy(network *net, data d); +void random_distort_image(image im, float hue, float saturation, float exposure); +void fill_image(image m, float s); +image grayscale_image(image im); +void rotate_image_cw(image im, int times); +double what_time_is_it_now(); +image rotate_image(image m, float rad); +void visualize_network(network *net); +float box_iou(box a, box b); +data load_all_cifar10(); +box_label *read_boxes(char *filename, int *n); +box float_to_box(float *f, int stride); +void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes); + +matrix network_predict_data(network *net, data test); +image **load_alphabet(); +image get_network_image(network *net); +float *network_predict(network *net, float *input); + +int network_width(network *net); +int network_height(network *net); +float *network_predict_image(network *net, image im); +void network_detect(network *net, image im, float thresh, float hier_thresh, float nms, detection *dets); +detection *get_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, int *num); +void free_detections(detection *dets, int n); + +void reset_network_state(network *net, int b); + +char **get_labels(char *filename); +void do_nms_obj(detection *dets, int total, int classes, float thresh); +void do_nms_sort(detection *dets, int total, int classes, float thresh); + +matrix make_matrix(int rows, int cols); + +#ifdef OPENCV +void *open_video_stream(const char *f, int c, int w, int h, int fps); +image get_image_from_stream(void *p); +void make_window(char *name, int w, int h, int fullscreen); +#endif + +void free_image(image m); +float train_network(network *net, data d); +pthread_t load_data_in_thread(load_args args); +void load_data_blocking(load_args args); +list *get_paths(char *filename); +void hierarchy_predictions(float *predictions, int n, tree *hier, int only_leaves, int stride); +void change_leaves(tree *t, char *leaf_list); + +int find_int_arg(int argc, char **argv, char *arg, int def); +float find_float_arg(int argc, char **argv, char *arg, float def); +int find_arg(int argc, char* argv[], char *arg); +char *find_char_arg(int argc, char **argv, char *arg, char *def); +char *basecfg(char *cfgfile); +void find_replace(char *str, char *orig, char *rep, char *output); +void free_ptrs(void **ptrs, int n); +char *fgetl(FILE *fp); +void strip(char *s); +float sec(clock_t clocks); +void **list_to_array(list *l); +void top_k(float *a, int n, int k, int *index); +int *read_map(char *filename); +void error(const char *s); +int max_index(float *a, int n); +int max_int_index(int *a, int n); +int sample_array(float *a, int n); +int *random_index_order(int min, int max); +void free_list(list *l); +float mse_array(float *a, int n); +float variance_array(float *a, int n); +float mag_array(float *a, int n); +void scale_array(float *a, int n, float s); +float mean_array(float *a, int n); +float sum_array(float *a, int n); +void normalize_array(float *a, int n); +int *read_intlist(char *s, int *n, int d); +size_t rand_size_t(); +float rand_normal(); +float rand_uniform(float min, float max); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/image.darknet/inst/include/darknet/python/darknet.py b/image.darknet/inst/include/darknet/python/darknet.py new file mode 100644 index 0000000..88d84cd --- /dev/null +++ b/image.darknet/inst/include/darknet/python/darknet.py @@ -0,0 +1,156 @@ +from ctypes import * +import math +import random + +def sample(probs): + s = sum(probs) + probs = [a/s for a in probs] + r = random.uniform(0, 1) + for i in range(len(probs)): + r = r - probs[i] + if r <= 0: + return i + return len(probs)-1 + +def c_array(ctype, values): + arr = (ctype*len(values))() + arr[:] = values + return arr + +class BOX(Structure): + _fields_ = [("x", c_float), + ("y", c_float), + ("w", c_float), + ("h", c_float)] + +class DETECTION(Structure): + _fields_ = [("bbox", BOX), + ("classes", c_int), + ("prob", POINTER(c_float)), + ("mask", POINTER(c_float)), + ("objectness", c_float), + ("sort_class", c_int)] + + +class IMAGE(Structure): + _fields_ = [("w", c_int), + ("h", c_int), + ("c", c_int), + ("data", POINTER(c_float))] + +class METADATA(Structure): + _fields_ = [("classes", c_int), + ("names", POINTER(c_char_p))] + + + +#lib = CDLL("/home/pjreddie/documents/darknet/libdarknet.so", RTLD_GLOBAL) +lib = CDLL("libdarknet.so", RTLD_GLOBAL) +lib.network_width.argtypes = [c_void_p] +lib.network_width.restype = c_int +lib.network_height.argtypes = [c_void_p] +lib.network_height.restype = c_int + +predict = lib.network_predict +predict.argtypes = [c_void_p, POINTER(c_float)] +predict.restype = POINTER(c_float) + +set_gpu = lib.cuda_set_device +set_gpu.argtypes = [c_int] + +make_image = lib.make_image +make_image.argtypes = [c_int, c_int, c_int] +make_image.restype = IMAGE + +get_network_boxes = lib.get_network_boxes +get_network_boxes.argtypes = [c_void_p, c_int, c_int, c_float, c_float, POINTER(c_int), c_int, POINTER(c_int)] +get_network_boxes.restype = POINTER(DETECTION) + +make_network_boxes = lib.make_network_boxes +make_network_boxes.argtypes = [c_void_p] +make_network_boxes.restype = POINTER(DETECTION) + +free_detections = lib.free_detections +free_detections.argtypes = [POINTER(DETECTION), c_int] + +free_ptrs = lib.free_ptrs +free_ptrs.argtypes = [POINTER(c_void_p), c_int] + +network_predict = lib.network_predict +network_predict.argtypes = [c_void_p, POINTER(c_float)] + +reset_rnn = lib.reset_rnn +reset_rnn.argtypes = [c_void_p] + +load_net = lib.load_network +load_net.argtypes = [c_char_p, c_char_p, c_int] +load_net.restype = c_void_p + +do_nms_obj = lib.do_nms_obj +do_nms_obj.argtypes = [POINTER(DETECTION), c_int, c_int, c_float] + +do_nms_sort = lib.do_nms_sort +do_nms_sort.argtypes = [POINTER(DETECTION), c_int, c_int, c_float] + +free_image = lib.free_image +free_image.argtypes = [IMAGE] + +letterbox_image = lib.letterbox_image +letterbox_image.argtypes = [IMAGE, c_int, c_int] +letterbox_image.restype = IMAGE + +load_meta = lib.get_metadata +lib.get_metadata.argtypes = [c_char_p] +lib.get_metadata.restype = METADATA + +load_image = lib.load_image_color +load_image.argtypes = [c_char_p, c_int, c_int] +load_image.restype = IMAGE + +rgbgr_image = lib.rgbgr_image +rgbgr_image.argtypes = [IMAGE] + +predict_image = lib.network_predict_image +predict_image.argtypes = [c_void_p, IMAGE] +predict_image.restype = POINTER(c_float) + +def classify(net, meta, im): + out = predict_image(net, im) + res = [] + for i in range(meta.classes): + res.append((meta.names[i], out[i])) + res = sorted(res, key=lambda x: -x[1]) + return res + +def detect(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45): + im = load_image(image, 0, 0) + num = c_int(0) + pnum = pointer(num) + predict_image(net, im) + dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, None, 0, pnum) + num = pnum[0] + if (nms): do_nms_obj(dets, num, meta.classes, nms); + + res = [] + for j in range(num): + for i in range(meta.classes): + if dets[j].prob[i] > 0: + b = dets[j].bbox + res.append((meta.names[i], dets[j].prob[i], (b.x, b.y, b.w, b.h))) + res = sorted(res, key=lambda x: -x[1]) + free_image(im) + free_detections(dets, num) + return res + +if __name__ == "__main__": + #net = load_net("cfg/densenet201.cfg", "/home/pjreddie/trained/densenet201.weights", 0) + #im = load_image("data/wolf.jpg", 0, 0) + #meta = load_meta("cfg/imagenet1k.data") + #r = classify(net, meta, im) + #print r[:10] + net = load_net("cfg/tiny-yolo.cfg", "tiny-yolo.weights", 0) + meta = load_meta("cfg/coco.data") + r = detect(net, meta, "data/dog.jpg") + print r + + diff --git a/image.darknet/inst/include/darknet/python/proverbot.py b/image.darknet/inst/include/darknet/python/proverbot.py new file mode 100644 index 0000000..095aae8 --- /dev/null +++ b/image.darknet/inst/include/darknet/python/proverbot.py @@ -0,0 +1,37 @@ +from darknet import * + +def predict_tactic(net, s): + prob = 0 + d = c_array(c_float, [0.0]*256) + tac = '' + if not len(s): + s = '\n' + for c in s[:-1]: + d[ord(c)] = 1 + pred = predict(net, d) + d[ord(c)] = 0 + c = s[-1] + while 1: + d[ord(c)] = 1 + pred = predict(net, d) + d[ord(c)] = 0 + pred = [pred[i] for i in range(256)] + ind = sample(pred) + c = chr(ind) + prob += math.log(pred[ind]) + if len(tac) and tac[-1] == '.': + break + tac = tac + c + return (tac, prob) + +def predict_tactics(net, s, n): + tacs = [] + for i in range(n): + reset_rnn(net) + tacs.append(predict_tactic(net, s)) + tacs = sorted(tacs, key=lambda x: -x[1]) + return tacs + +net = load_net("cfg/coq.test.cfg", "/home/pjreddie/backup/coq.backup", 0) +t = predict_tactics(net, "+++++\n", 10) +print t diff --git a/image.darknet/inst/include/darknet/scripts/get_coco_dataset.sh b/image.darknet/inst/include/darknet/scripts/get_coco_dataset.sh new file mode 100644 index 0000000..2846301 --- /dev/null +++ b/image.darknet/inst/include/darknet/scripts/get_coco_dataset.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Clone COCO API +git clone https://github.com/pdollar/coco +cd coco + +mkdir images +cd images + +# Download Images +wget -c https://pjreddie.com/media/files/train2014.zip +wget -c https://pjreddie.com/media/files/val2014.zip + +# Unzip +unzip -q train2014.zip +unzip -q val2014.zip + +cd .. + +# Download COCO Metadata +wget -c https://pjreddie.com/media/files/instances_train-val2014.zip +wget -c https://pjreddie.com/media/files/coco/5k.part +wget -c https://pjreddie.com/media/files/coco/trainvalno5k.part +wget -c https://pjreddie.com/media/files/coco/labels.tgz +tar xzf labels.tgz +unzip -q instances_train-val2014.zip + +# Set Up Image Lists +paste <(awk "{print \"$PWD\"}" <5k.part) 5k.part | tr -d '\t' > 5k.txt +paste <(awk "{print \"$PWD\"}" trainvalno5k.txt + diff --git a/image.darknet/inst/include/darknet/scripts/voc_label.py b/image.darknet/inst/include/darknet/scripts/voc_label.py index d1e8823..679fc36 100644 --- a/image.darknet/inst/include/darknet/scripts/voc_label.py +++ b/image.darknet/inst/include/darknet/scripts/voc_label.py @@ -10,10 +10,10 @@ def convert(size, box): - dw = 1./size[0] - dh = 1./size[1] - x = (box[0] + box[1])/2.0 - y = (box[2] + box[3])/2.0 + dw = 1./(size[0]) + dh = 1./(size[1]) + x = (box[0] + box[1])/2.0 - 1 + y = (box[2] + box[3])/2.0 - 1 w = box[1] - box[0] h = box[3] - box[2] x = x*dw @@ -34,7 +34,7 @@ def convert_annotation(year, image_id): for obj in root.iter('object'): difficult = obj.find('difficult').text cls = obj.find('name').text - if cls not in classes or int(difficult) == 1: + if cls not in classes or int(difficult)==1: continue cls_id = classes.index(cls) xmlbox = obj.find('bndbox') @@ -54,3 +54,6 @@ def convert_annotation(year, image_id): convert_annotation(year, image_id) list_file.close() +os.system("cat 2007_train.txt 2007_val.txt 2012_train.txt 2012_val.txt > train.txt") +os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt > train.all.txt") + diff --git a/image.darknet/inst/include/darknet/src/activation_kernels.cu b/image.darknet/inst/include/darknet/src/activation_kernels.cu index 994e206..4dc5804 100644 --- a/image.darknet/inst/include/darknet/src/activation_kernels.cu +++ b/image.darknet/inst/include/darknet/src/activation_kernels.cu @@ -10,8 +10,8 @@ extern "C" { __device__ float lhtan_activate_kernel(float x) { - if(x < 0) return .001*x; - if(x > 1) return .001*(x-1) + 1; + if(x < 0) return .001f*x; + if(x > 1) return .001f*(x-1.f) + 1.f; return x; } __device__ float lhtan_gradient_kernel(float x) @@ -27,25 +27,26 @@ __device__ float hardtan_activate_kernel(float x) return x; } __device__ float linear_activate_kernel(float x){return x;} -__device__ float logistic_activate_kernel(float x){return 1./(1. + exp(-x));} -__device__ float loggy_activate_kernel(float x){return 2./(1. + exp(-x)) - 1;} +__device__ float logistic_activate_kernel(float x){return 1.f/(1.f + expf(-x));} +__device__ float loggy_activate_kernel(float x){return 2.f/(1.f + expf(-x)) - 1;} __device__ float relu_activate_kernel(float x){return x*(x>0);} -__device__ float elu_activate_kernel(float x){return (x >= 0)*x + (x < 0)*(exp(x)-1);} -__device__ float relie_activate_kernel(float x){return (x>0) ? x : .01*x;} -__device__ float ramp_activate_kernel(float x){return x*(x>0)+.1*x;} -__device__ float leaky_activate_kernel(float x){return (x>0) ? x : .1*x;} -__device__ float tanh_activate_kernel(float x){return (2/(1 + exp(-2*x)) - 1);} +__device__ float elu_activate_kernel(float x){return (x >= 0)*x + (x < 0)*(expf(x)-1);} +__device__ float selu_activate_kernel(float x){return (x >= 0)*1.0507f*x + (x < 0)*1.0507f*1.6732f*(expf(x)-1);} +__device__ float relie_activate_kernel(float x){return (x>0) ? x : .01f*x;} +__device__ float ramp_activate_kernel(float x){return x*(x>0)+.1f*x;} +__device__ float leaky_activate_kernel(float x){return (x>0) ? x : .1f*x;} +__device__ float tanh_activate_kernel(float x){return (2.f/(1 + expf(-2*x)) - 1);} __device__ float plse_activate_kernel(float x) { - if(x < -4) return .01 * (x + 4); - if(x > 4) return .01 * (x - 4) + 1; - return .125*x + .5; + if(x < -4) return .01f * (x + 4); + if(x > 4) return .01f * (x - 4) + 1; + return .125f*x + .5f; } __device__ float stair_activate_kernel(float x) { - int n = floor(x); - if (n%2 == 0) return floor(x/2.); - else return (x - n) + floor(x/2.); + int n = floorf(x); + if (n%2 == 0) return floorf(x/2); + else return (x - n) + floorf(x/2); } @@ -58,19 +59,20 @@ __device__ float linear_gradient_kernel(float x){return 1;} __device__ float logistic_gradient_kernel(float x){return (1-x)*x;} __device__ float loggy_gradient_kernel(float x) { - float y = (x+1.)/2.; + float y = (x+1)/2; return 2*(1-y)*y; } __device__ float relu_gradient_kernel(float x){return (x>0);} __device__ float elu_gradient_kernel(float x){return (x >= 0) + (x < 0)*(x + 1);} -__device__ float relie_gradient_kernel(float x){return (x>0) ? 1 : .01;} -__device__ float ramp_gradient_kernel(float x){return (x>0)+.1;} -__device__ float leaky_gradient_kernel(float x){return (x>0) ? 1 : .1;} +__device__ float selu_gradient_kernel(float x){return (x >= 0)*1.0507 + (x < 0)*(x + 1.0507*1.6732);} +__device__ float relie_gradient_kernel(float x){return (x>0) ? 1 : .01f;} +__device__ float ramp_gradient_kernel(float x){return (x>0)+.1f;} +__device__ float leaky_gradient_kernel(float x){return (x>0) ? 1 : .1f;} __device__ float tanh_gradient_kernel(float x){return 1-x*x;} -__device__ float plse_gradient_kernel(float x){return (x < 0 || x > 1) ? .01 : .125;} +__device__ float plse_gradient_kernel(float x){return (x < 0 || x > 1) ? .01f : .125f;} __device__ float stair_gradient_kernel(float x) { - if (floor(x) == x) return 0; + if (floorf(x) == x) return 0; return 1; } @@ -87,6 +89,8 @@ __device__ float activate_kernel(float x, ACTIVATION a) return relu_activate_kernel(x); case ELU: return elu_activate_kernel(x); + case SELU: + return selu_activate_kernel(x); case RELIE: return relie_activate_kernel(x); case RAMP: @@ -120,6 +124,8 @@ __device__ float gradient_kernel(float x, ACTIVATION a) return relu_gradient_kernel(x); case ELU: return elu_gradient_kernel(x); + case SELU: + return selu_gradient_kernel(x); case RELIE: return relie_gradient_kernel(x); case RAMP: @@ -140,6 +146,41 @@ __device__ float gradient_kernel(float x, ACTIVATION a) return 0; } +__global__ void binary_gradient_array_kernel(float *x, float *dy, int n, int s, BINARY_ACTIVATION a, float *dx) +{ + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + int i = id % s; + int b = id / s; + float x1 = x[b*s + i]; + float x2 = x[b*s + s/2 + i]; + if(id < n) { + float de = dy[id]; + dx[b*s + i] = x2*de; + dx[b*s + s/2 + i] = x1*de; + } +} + +extern "C" void binary_gradient_array_gpu(float *x, float *dx, int n, int size, BINARY_ACTIVATION a, float *y) +{ + binary_gradient_array_kernel<<>>(x, dx, n/2, size, a, y); + check_error(cudaPeekAtLastError()); +} +__global__ void binary_activate_array_kernel(float *x, int n, int s, BINARY_ACTIVATION a, float *y) +{ + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + int i = id % s; + int b = id / s; + float x1 = x[b*s + i]; + float x2 = x[b*s + s/2 + i]; + if(id < n) y[id] = x1*x2; +} + +extern "C" void binary_activate_array_gpu(float *x, int n, int size, BINARY_ACTIVATION a, float *y) +{ + binary_activate_array_kernel<<>>(x, n/2, size, a, y); + check_error(cudaPeekAtLastError()); +} + __global__ void activate_array_kernel(float *x, int n, ACTIVATION a) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; @@ -152,13 +193,13 @@ __global__ void gradient_array_kernel(float *x, int n, ACTIVATION a, float *delt if(i < n) delta[i] *= gradient_kernel(x[i], a); } -extern "C" void activate_array_ongpu(float *x, int n, ACTIVATION a) +extern "C" void activate_array_gpu(float *x, int n, ACTIVATION a) { activate_array_kernel<<>>(x, n, a); check_error(cudaPeekAtLastError()); } -extern "C" void gradient_array_ongpu(float *x, int n, ACTIVATION a, float *delta) +extern "C" void gradient_array_gpu(float *x, int n, ACTIVATION a, float *delta) { gradient_array_kernel<<>>(x, n, a, delta); check_error(cudaPeekAtLastError()); diff --git a/image.darknet/inst/include/darknet/src/activation_layer.c b/image.darknet/inst/include/darknet/src/activation_layer.c index 3430dac..b4ba953 100644 --- a/image.darknet/inst/include/darknet/src/activation_layer.c +++ b/image.darknet/inst/include/darknet/src/activation_layer.c @@ -35,29 +35,29 @@ layer make_activation_layer(int batch, int inputs, ACTIVATION activation) return l; } -void forward_activation_layer(layer l, network_state state) +void forward_activation_layer(layer l, network net) { - copy_cpu(l.outputs*l.batch, state.input, 1, l.output, 1); + copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); activate_array(l.output, l.outputs*l.batch, l.activation); } -void backward_activation_layer(layer l, network_state state) +void backward_activation_layer(layer l, network net) { gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); - copy_cpu(l.outputs*l.batch, l.delta, 1, state.delta, 1); + copy_cpu(l.outputs*l.batch, l.delta, 1, net.delta, 1); } #ifdef GPU -void forward_activation_layer_gpu(layer l, network_state state) +void forward_activation_layer_gpu(layer l, network net) { - copy_ongpu(l.outputs*l.batch, state.input, 1, l.output_gpu, 1); - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); } -void backward_activation_layer_gpu(layer l, network_state state) +void backward_activation_layer_gpu(layer l, network net) { - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); - copy_ongpu(l.outputs*l.batch, l.delta_gpu, 1, state.delta, 1); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + copy_gpu(l.outputs*l.batch, l.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/inst/include/darknet/src/activation_layer.h b/image.darknet/inst/include/darknet/src/activation_layer.h index a09756a..42118a8 100644 --- a/image.darknet/inst/include/darknet/src/activation_layer.h +++ b/image.darknet/inst/include/darknet/src/activation_layer.h @@ -7,12 +7,12 @@ layer make_activation_layer(int batch, int inputs, ACTIVATION activation); -void forward_activation_layer(layer l, network_state state); -void backward_activation_layer(layer l, network_state state); +void forward_activation_layer(layer l, network net); +void backward_activation_layer(layer l, network net); #ifdef GPU -void forward_activation_layer_gpu(layer l, network_state state); -void backward_activation_layer_gpu(layer l, network_state state); +void forward_activation_layer_gpu(layer l, network net); +void backward_activation_layer_gpu(layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/activations.c b/image.darknet/inst/include/darknet/src/activations.c index 0cbb2f5..da1a17a 100644 --- a/image.darknet/inst/include/darknet/src/activations.c +++ b/image.darknet/inst/include/darknet/src/activations.c @@ -16,6 +16,8 @@ char *get_activation_string(ACTIVATION a) return "relu"; case ELU: return "elu"; + case SELU: + return "selu"; case RELIE: return "relie"; case RAMP: @@ -46,6 +48,7 @@ ACTIVATION get_activation(char *s) if (strcmp(s, "loggy")==0) return LOGGY; if (strcmp(s, "relu")==0) return RELU; if (strcmp(s, "elu")==0) return ELU; + if (strcmp(s, "selu")==0) return SELU; if (strcmp(s, "relie")==0) return RELIE; if (strcmp(s, "plse")==0) return PLSE; if (strcmp(s, "hardtan")==0) return HARDTAN; @@ -72,6 +75,8 @@ float activate(float x, ACTIVATION a) return relu_activate(x); case ELU: return elu_activate(x); + case SELU: + return selu_activate(x); case RELIE: return relie_activate(x); case RAMP: @@ -113,6 +118,8 @@ float gradient(float x, ACTIVATION a) return relu_gradient(x); case ELU: return elu_gradient(x); + case SELU: + return selu_gradient(x); case RELIE: return relie_gradient(x); case RAMP: diff --git a/image.darknet/inst/include/darknet/src/activations.h b/image.darknet/inst/include/darknet/src/activations.h index 1c36ff5..9780d2c 100644 --- a/image.darknet/inst/include/darknet/src/activations.h +++ b/image.darknet/inst/include/darknet/src/activations.h @@ -1,12 +1,9 @@ #ifndef ACTIVATIONS_H #define ACTIVATIONS_H +#include "darknet.h" #include "cuda.h" #include "math.h" -typedef enum{ - LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN -}ACTIVATION; - ACTIVATION get_activation(char *s); char *get_activation_string(ACTIVATION a); @@ -15,8 +12,8 @@ float gradient(float x, ACTIVATION a); void gradient_array(const float *x, const int n, const ACTIVATION a, float *delta); void activate_array(float *x, const int n, const ACTIVATION a); #ifdef GPU -void activate_array_ongpu(float *x, int n, ACTIVATION a); -void gradient_array_ongpu(float *x, int n, ACTIVATION a, float *delta); +void activate_array_gpu(float *x, int n, ACTIVATION a); +void gradient_array_gpu(float *x, int n, ACTIVATION a, float *delta); #endif static inline float stair_activate(float x) @@ -36,6 +33,7 @@ static inline float logistic_activate(float x){return 1./(1. + exp(-x));} static inline float loggy_activate(float x){return 2./(1. + exp(-x)) - 1;} static inline float relu_activate(float x){return x*(x>0);} static inline float elu_activate(float x){return (x >= 0)*x + (x < 0)*(exp(x)-1);} +static inline float selu_activate(float x){return (x >= 0)*1.0507*x + (x < 0)*1.0507*1.6732*(exp(x)-1);} static inline float relie_activate(float x){return (x>0) ? x : .01*x;} static inline float ramp_activate(float x){return x*(x>0)+.1*x;} static inline float leaky_activate(float x){return (x>0) ? x : .1*x;} @@ -78,6 +76,7 @@ static inline float stair_gradient(float x) } static inline float relu_gradient(float x){return (x>0);} static inline float elu_gradient(float x){return (x >= 0) + (x < 0)*(x + 1);} +static inline float selu_gradient(float x){return (x >= 0)*1.0507 + (x < 0)*(x + 1.0507*1.6732);} static inline float relie_gradient(float x){return (x>0) ? 1 : .01;} static inline float ramp_gradient(float x){return (x>0)+.1;} static inline float leaky_gradient(float x){return (x>0) ? 1 : .1;} diff --git a/image.darknet/inst/include/darknet/src/avgpool_layer.c b/image.darknet/inst/include/darknet/src/avgpool_layer.c index b6932fe..83034db 100644 --- a/image.darknet/inst/include/darknet/src/avgpool_layer.c +++ b/image.darknet/inst/include/darknet/src/avgpool_layer.c @@ -37,7 +37,7 @@ void resize_avgpool_layer(avgpool_layer *l, int w, int h) l->inputs = h*w*l->c; } -void forward_avgpool_layer(const avgpool_layer l, network_state state) +void forward_avgpool_layer(const avgpool_layer l, network net) { int b,i,k; @@ -47,14 +47,14 @@ void forward_avgpool_layer(const avgpool_layer l, network_state state) l.output[out_index] = 0; for(i = 0; i < l.h*l.w; ++i){ int in_index = i + l.h*l.w*(k + b*l.c); - l.output[out_index] += state.input[in_index]; + l.output[out_index] += net.input[in_index]; } l.output[out_index] /= l.h*l.w; } } } -void backward_avgpool_layer(const avgpool_layer l, network_state state) +void backward_avgpool_layer(const avgpool_layer l, network net) { int b,i,k; @@ -63,7 +63,7 @@ void backward_avgpool_layer(const avgpool_layer l, network_state state) int out_index = k + b*l.c; for(i = 0; i < l.h*l.w; ++i){ int in_index = i + l.h*l.w*(k + b*l.c); - state.delta[in_index] += l.delta[out_index] / (l.h*l.w); + net.delta[in_index] += l.delta[out_index] / (l.h*l.w); } } } diff --git a/image.darknet/inst/include/darknet/src/avgpool_layer.h b/image.darknet/inst/include/darknet/src/avgpool_layer.h index f8329ae..3bd356c 100644 --- a/image.darknet/inst/include/darknet/src/avgpool_layer.h +++ b/image.darknet/inst/include/darknet/src/avgpool_layer.h @@ -11,12 +11,12 @@ typedef layer avgpool_layer; image get_avgpool_image(avgpool_layer l); avgpool_layer make_avgpool_layer(int batch, int w, int h, int c); void resize_avgpool_layer(avgpool_layer *l, int w, int h); -void forward_avgpool_layer(const avgpool_layer l, network_state state); -void backward_avgpool_layer(const avgpool_layer l, network_state state); +void forward_avgpool_layer(const avgpool_layer l, network net); +void backward_avgpool_layer(const avgpool_layer l, network net); #ifdef GPU -void forward_avgpool_layer_gpu(avgpool_layer l, network_state state); -void backward_avgpool_layer_gpu(avgpool_layer l, network_state state); +void forward_avgpool_layer_gpu(avgpool_layer l, network net); +void backward_avgpool_layer_gpu(avgpool_layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/avgpool_layer_kernels.cu b/image.darknet/inst/include/darknet/src/avgpool_layer_kernels.cu index b7e2770..a7eca3a 100644 --- a/image.darknet/inst/include/darknet/src/avgpool_layer_kernels.cu +++ b/image.darknet/inst/include/darknet/src/avgpool_layer_kernels.cu @@ -43,19 +43,19 @@ __global__ void backward_avgpool_layer_kernel(int n, int w, int h, int c, float } } -extern "C" void forward_avgpool_layer_gpu(avgpool_layer layer, network_state state) +extern "C" void forward_avgpool_layer_gpu(avgpool_layer layer, network net) { size_t n = layer.c*layer.batch; - forward_avgpool_layer_kernel<<>>(n, layer.w, layer.h, layer.c, state.input, layer.output_gpu); + forward_avgpool_layer_kernel<<>>(n, layer.w, layer.h, layer.c, net.input_gpu, layer.output_gpu); check_error(cudaPeekAtLastError()); } -extern "C" void backward_avgpool_layer_gpu(avgpool_layer layer, network_state state) +extern "C" void backward_avgpool_layer_gpu(avgpool_layer layer, network net) { size_t n = layer.c*layer.batch; - backward_avgpool_layer_kernel<<>>(n, layer.w, layer.h, layer.c, state.delta, layer.delta_gpu); + backward_avgpool_layer_kernel<<>>(n, layer.w, layer.h, layer.c, net.delta_gpu, layer.delta_gpu); check_error(cudaPeekAtLastError()); } diff --git a/image.darknet/inst/include/darknet/src/batchnorm_layer.c b/image.darknet/inst/include/darknet/src/batchnorm_layer.c index b53548b..ebff387 100644 --- a/image.darknet/inst/include/darknet/src/batchnorm_layer.c +++ b/image.darknet/inst/include/darknet/src/batchnorm_layer.c @@ -1,3 +1,4 @@ +#include "convolutional_layer.h" #include "batchnorm_layer.h" #include "blas.h" #include @@ -5,55 +6,67 @@ layer make_batchnorm_layer(int batch, int w, int h, int c) { fprintf(stderr, "Batch Normalization Layer: %d x %d x %d image\n", w,h,c); - layer layer = {0}; - layer.type = BATCHNORM; - layer.batch = batch; - layer.h = layer.out_h = h; - layer.w = layer.out_w = w; - layer.c = layer.out_c = c; - layer.output = calloc(h * w * c * batch, sizeof(float)); - layer.delta = calloc(h * w * c * batch, sizeof(float)); - layer.inputs = w*h*c; - layer.outputs = layer.inputs; - - layer.scales = calloc(c, sizeof(float)); - layer.scale_updates = calloc(c, sizeof(float)); + layer l = {0}; + l.type = BATCHNORM; + l.batch = batch; + l.h = l.out_h = h; + l.w = l.out_w = w; + l.c = l.out_c = c; + l.output = calloc(h * w * c * batch, sizeof(float)); + l.delta = calloc(h * w * c * batch, sizeof(float)); + l.inputs = w*h*c; + l.outputs = l.inputs; + + l.scales = calloc(c, sizeof(float)); + l.scale_updates = calloc(c, sizeof(float)); + l.biases = calloc(c, sizeof(float)); + l.bias_updates = calloc(c, sizeof(float)); int i; for(i = 0; i < c; ++i){ - layer.scales[i] = 1; + l.scales[i] = 1; } - layer.mean = calloc(c, sizeof(float)); - layer.variance = calloc(c, sizeof(float)); + l.mean = calloc(c, sizeof(float)); + l.variance = calloc(c, sizeof(float)); - layer.rolling_mean = calloc(c, sizeof(float)); - layer.rolling_variance = calloc(c, sizeof(float)); + l.rolling_mean = calloc(c, sizeof(float)); + l.rolling_variance = calloc(c, sizeof(float)); - layer.forward = forward_batchnorm_layer; - layer.backward = backward_batchnorm_layer; + l.forward = forward_batchnorm_layer; + l.backward = backward_batchnorm_layer; #ifdef GPU - layer.forward_gpu = forward_batchnorm_layer_gpu; - layer.backward_gpu = backward_batchnorm_layer_gpu; + l.forward_gpu = forward_batchnorm_layer_gpu; + l.backward_gpu = backward_batchnorm_layer_gpu; + + l.output_gpu = cuda_make_array(l.output, h * w * c * batch); + l.delta_gpu = cuda_make_array(l.delta, h * w * c * batch); + + l.biases_gpu = cuda_make_array(l.biases, c); + l.bias_updates_gpu = cuda_make_array(l.bias_updates, c); - layer.output_gpu = cuda_make_array(layer.output, h * w * c * batch); - layer.delta_gpu = cuda_make_array(layer.delta, h * w * c * batch); + l.scales_gpu = cuda_make_array(l.scales, c); + l.scale_updates_gpu = cuda_make_array(l.scale_updates, c); - layer.scales_gpu = cuda_make_array(layer.scales, c); - layer.scale_updates_gpu = cuda_make_array(layer.scale_updates, c); + l.mean_gpu = cuda_make_array(l.mean, c); + l.variance_gpu = cuda_make_array(l.variance, c); - layer.mean_gpu = cuda_make_array(layer.mean, c); - layer.variance_gpu = cuda_make_array(layer.variance, c); + l.rolling_mean_gpu = cuda_make_array(l.mean, c); + l.rolling_variance_gpu = cuda_make_array(l.variance, c); - layer.rolling_mean_gpu = cuda_make_array(layer.mean, c); - layer.rolling_variance_gpu = cuda_make_array(layer.variance, c); + l.mean_delta_gpu = cuda_make_array(l.mean, c); + l.variance_delta_gpu = cuda_make_array(l.variance, c); - layer.mean_delta_gpu = cuda_make_array(layer.mean, c); - layer.variance_delta_gpu = cuda_make_array(layer.variance, c); + l.x_gpu = cuda_make_array(l.output, l.batch*l.outputs); + l.x_norm_gpu = cuda_make_array(l.output, l.batch*l.outputs); + #ifdef CUDNN + cudnnCreateTensorDescriptor(&l.normTensorDesc); + cudnnCreateTensorDescriptor(&l.dstTensorDesc); + cudnnSetTensor4dDescriptor(l.dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l.batch, l.out_c, l.out_h, l.out_w); + cudnnSetTensor4dDescriptor(l.normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l.out_c, 1, 1); - layer.x_gpu = cuda_make_array(layer.output, layer.batch*layer.outputs); - layer.x_norm_gpu = cuda_make_array(layer.output, layer.batch*layer.outputs); + #endif #endif - return layer; + return l; } void backward_scale_cpu(float *x_norm, float *delta, int batch, int n, int size, float *scale_updates) @@ -108,7 +121,7 @@ void normalize_delta_cpu(float *x, float *mean, float *variance, float *mean_del for(f = 0; f < filters; ++f){ for(k = 0; k < spatial; ++k){ int index = j*filters*spatial + f*spatial + k; - delta[index] = delta[index] * 1./(sqrt(variance[f]) + .00001f) + variance_delta[f] * 2. * (x[index] - mean[f]) / (spatial * batch) + mean_delta[f]/(spatial*batch); + delta[index] = delta[index] * 1./(sqrt(variance[f] + .00001f)) + variance_delta[f] * 2. * (x[index] - mean[f]) / (spatial * batch) + mean_delta[f]/(spatial*batch); } } } @@ -119,33 +132,35 @@ void resize_batchnorm_layer(layer *layer, int w, int h) fprintf(stderr, "Not implemented\n"); } -void forward_batchnorm_layer(layer l, network_state state) +void forward_batchnorm_layer(layer l, network net) { - if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, state.input, 1, l.output, 1); - if(l.type == CONNECTED){ - l.out_c = l.outputs; - l.out_h = l.out_w = 1; - } - if(state.train){ + if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); + copy_cpu(l.outputs*l.batch, l.output, 1, l.x, 1); + if(net.train){ mean_cpu(l.output, l.batch, l.out_c, l.out_h*l.out_w, l.mean); variance_cpu(l.output, l.mean, l.batch, l.out_c, l.out_h*l.out_w, l.variance); - scal_cpu(l.out_c, .9, l.rolling_mean, 1); - axpy_cpu(l.out_c, .1, l.mean, 1, l.rolling_mean, 1); - scal_cpu(l.out_c, .9, l.rolling_variance, 1); - axpy_cpu(l.out_c, .1, l.variance, 1, l.rolling_variance, 1); + scal_cpu(l.out_c, .99, l.rolling_mean, 1); + axpy_cpu(l.out_c, .01, l.mean, 1, l.rolling_mean, 1); + scal_cpu(l.out_c, .99, l.rolling_variance, 1); + axpy_cpu(l.out_c, .01, l.variance, 1, l.rolling_variance, 1); - copy_cpu(l.outputs*l.batch, l.output, 1, l.x, 1); normalize_cpu(l.output, l.mean, l.variance, l.batch, l.out_c, l.out_h*l.out_w); copy_cpu(l.outputs*l.batch, l.output, 1, l.x_norm, 1); } else { normalize_cpu(l.output, l.rolling_mean, l.rolling_variance, l.batch, l.out_c, l.out_h*l.out_w); } scale_bias(l.output, l.scales, l.batch, l.out_c, l.out_h*l.out_w); + add_bias(l.output, l.biases, l.batch, l.out_c, l.out_h*l.out_w); } -void backward_batchnorm_layer(const layer l, network_state state) +void backward_batchnorm_layer(layer l, network net) { + if(!net.train){ + l.mean = l.rolling_mean; + l.variance = l.rolling_variance; + } + backward_bias(l.bias_updates, l.delta, l.batch, l.out_c, l.out_w*l.out_h); backward_scale_cpu(l.x_norm, l.delta, l.batch, l.out_c, l.out_w*l.out_h, l.scale_updates); scale_bias(l.delta, l.scales, l.batch, l.out_c, l.out_h*l.out_w); @@ -153,7 +168,7 @@ void backward_batchnorm_layer(const layer l, network_state state) mean_delta_cpu(l.delta, l.variance, l.batch, l.out_c, l.out_w*l.out_h, l.mean_delta); variance_delta_cpu(l.x, l.delta, l.mean, l.variance, l.batch, l.out_c, l.out_w*l.out_h, l.variance_delta); normalize_delta_cpu(l.x, l.mean, l.variance, l.mean_delta, l.variance_delta, l.batch, l.out_c, l.out_w*l.out_h, l.delta); - if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, l.delta, 1, state.delta, 1); + if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, l.delta, 1, net.delta, 1); } #ifdef GPU @@ -171,34 +186,86 @@ void push_batchnorm_layer(layer l) cuda_push_array(l.rolling_variance_gpu, l.rolling_variance, l.c); } -void forward_batchnorm_layer_gpu(layer l, network_state state) +void forward_batchnorm_layer_gpu(layer l, network net) { - if(l.type == BATCHNORM) copy_ongpu(l.outputs*l.batch, state.input, 1, l.output_gpu, 1); - if(l.type == CONNECTED){ - l.out_c = l.outputs; - l.out_h = l.out_w = 1; - } - if (state.train) { + if(l.type == BATCHNORM) copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.x_gpu, 1); + if (net.train) { +#ifdef CUDNN + float one = 1; + float zero = 0; + cudnnBatchNormalizationForwardTraining(cudnn_handle(), + CUDNN_BATCHNORM_SPATIAL, + &one, + &zero, + l.dstTensorDesc, + l.x_gpu, + l.dstTensorDesc, + l.output_gpu, + l.normTensorDesc, + l.scales_gpu, + l.biases_gpu, + .01, + l.rolling_mean_gpu, + l.rolling_variance_gpu, + .00001, + l.mean_gpu, + l.variance_gpu); +#else fast_mean_gpu(l.output_gpu, l.batch, l.out_c, l.out_h*l.out_w, l.mean_gpu); fast_variance_gpu(l.output_gpu, l.mean_gpu, l.batch, l.out_c, l.out_h*l.out_w, l.variance_gpu); - scal_ongpu(l.out_c, .99, l.rolling_mean_gpu, 1); - axpy_ongpu(l.out_c, .01, l.mean_gpu, 1, l.rolling_mean_gpu, 1); - scal_ongpu(l.out_c, .99, l.rolling_variance_gpu, 1); - axpy_ongpu(l.out_c, .01, l.variance_gpu, 1, l.rolling_variance_gpu, 1); + scal_gpu(l.out_c, .99, l.rolling_mean_gpu, 1); + axpy_gpu(l.out_c, .01, l.mean_gpu, 1, l.rolling_mean_gpu, 1); + scal_gpu(l.out_c, .99, l.rolling_variance_gpu, 1); + axpy_gpu(l.out_c, .01, l.variance_gpu, 1, l.rolling_variance_gpu, 1); - copy_ongpu(l.outputs*l.batch, l.output_gpu, 1, l.x_gpu, 1); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.x_gpu, 1); normalize_gpu(l.output_gpu, l.mean_gpu, l.variance_gpu, l.batch, l.out_c, l.out_h*l.out_w); - copy_ongpu(l.outputs*l.batch, l.output_gpu, 1, l.x_norm_gpu, 1); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.x_norm_gpu, 1); + + scale_bias_gpu(l.output_gpu, l.scales_gpu, l.batch, l.out_c, l.out_h*l.out_w); + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.out_c, l.out_w*l.out_h); +#endif } else { normalize_gpu(l.output_gpu, l.rolling_mean_gpu, l.rolling_variance_gpu, l.batch, l.out_c, l.out_h*l.out_w); + scale_bias_gpu(l.output_gpu, l.scales_gpu, l.batch, l.out_c, l.out_h*l.out_w); + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.out_c, l.out_w*l.out_h); } - scale_bias_gpu(l.output_gpu, l.scales_gpu, l.batch, l.out_c, l.out_h*l.out_w); } -void backward_batchnorm_layer_gpu(const layer l, network_state state) +void backward_batchnorm_layer_gpu(layer l, network net) { + if(!net.train){ + l.mean_gpu = l.rolling_mean_gpu; + l.variance_gpu = l.rolling_variance_gpu; + } +#ifdef CUDNN + float one = 1; + float zero = 0; + cudnnBatchNormalizationBackward(cudnn_handle(), + CUDNN_BATCHNORM_SPATIAL, + &one, + &zero, + &one, + &one, + l.dstTensorDesc, + l.x_gpu, + l.dstTensorDesc, + l.delta_gpu, + l.dstTensorDesc, + l.x_norm_gpu, + l.normTensorDesc, + l.scales_gpu, + l.scale_updates_gpu, + l.bias_updates_gpu, + .00001, + l.mean_gpu, + l.variance_gpu); + copy_gpu(l.outputs*l.batch, l.x_norm_gpu, 1, l.delta_gpu, 1); +#else + backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.out_c, l.out_w*l.out_h); backward_scale_gpu(l.x_norm_gpu, l.delta_gpu, l.batch, l.out_c, l.out_w*l.out_h, l.scale_updates_gpu); scale_bias_gpu(l.delta_gpu, l.scales_gpu, l.batch, l.out_c, l.out_h*l.out_w); @@ -206,6 +273,7 @@ void backward_batchnorm_layer_gpu(const layer l, network_state state) fast_mean_delta_gpu(l.delta_gpu, l.variance_gpu, l.batch, l.out_c, l.out_w*l.out_h, l.mean_delta_gpu); fast_variance_delta_gpu(l.x_gpu, l.delta_gpu, l.mean_gpu, l.variance_gpu, l.batch, l.out_c, l.out_w*l.out_h, l.variance_delta_gpu); normalize_delta_gpu(l.x_gpu, l.mean_gpu, l.variance_gpu, l.mean_delta_gpu, l.variance_delta_gpu, l.batch, l.out_c, l.out_w*l.out_h, l.delta_gpu); - if(l.type == BATCHNORM) copy_ongpu(l.outputs*l.batch, l.delta_gpu, 1, state.delta, 1); +#endif + if(l.type == BATCHNORM) copy_gpu(l.outputs*l.batch, l.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/inst/include/darknet/src/batchnorm_layer.h b/image.darknet/inst/include/darknet/src/batchnorm_layer.h index 99d1d0f..25a18a3 100644 --- a/image.darknet/inst/include/darknet/src/batchnorm_layer.h +++ b/image.darknet/inst/include/darknet/src/batchnorm_layer.h @@ -6,12 +6,12 @@ #include "network.h" layer make_batchnorm_layer(int batch, int w, int h, int c); -void forward_batchnorm_layer(layer l, network_state state); -void backward_batchnorm_layer(layer l, network_state state); +void forward_batchnorm_layer(layer l, network net); +void backward_batchnorm_layer(layer l, network net); #ifdef GPU -void forward_batchnorm_layer_gpu(layer l, network_state state); -void backward_batchnorm_layer_gpu(layer l, network_state state); +void forward_batchnorm_layer_gpu(layer l, network net); +void backward_batchnorm_layer_gpu(layer l, network net); void pull_batchnorm_layer(layer l); void push_batchnorm_layer(layer l); #endif diff --git a/image.darknet/inst/include/darknet/src/blas.c b/image.darknet/inst/include/darknet/src/blas.c index 31bd86b..9e16044 100644 --- a/image.darknet/inst/include/darknet/src/blas.c +++ b/image.darknet/inst/include/darknet/src/blas.c @@ -1,5 +1,6 @@ #include "blas.h" -#include "math.h" + +#include #include #include #include @@ -54,7 +55,17 @@ void weighted_sum_cpu(float *a, float *b, float *s, int n, float *c) } } -void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out) +void weighted_delta_cpu(float *a, float *b, float *s, float *da, float *db, float *ds, int n, float *dc) +{ + int i; + for(i = 0; i < n; ++i){ + if(da) da[i] += dc[i] * s[i]; + if(db) db[i] += dc[i] * (1-s[i]); + ds[i] += dc[i] * (a[i] - b[i]); + } +} + +void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out) { int stride = w1/w2; int sample = w2/w1; @@ -73,7 +84,7 @@ void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, for(i = 0; i < minw; ++i){ int out_index = i*sample + w2*(j*sample + h2*(k + c2*b)); int add_index = i*stride + w1*(j*stride + h1*(k + c1*b)); - out[out_index] += add[add_index]; + out[out_index] = s1*out[out_index] + s2*add[add_index]; } } } @@ -112,6 +123,27 @@ void variance_cpu(float *x, float *mean, int batch, int filters, int spatial, fl } } +void l2normalize_cpu(float *x, float *dx, int batch, int filters, int spatial) +{ + int b,f,i; + for(b = 0; b < batch; ++b){ + for(i = 0; i < spatial; ++i){ + float sum = 0; + for(f = 0; f < filters; ++f){ + int index = b*filters*spatial + f*spatial + i; + sum += powf(x[index], 2); + } + sum = sqrtf(sum); + for(f = 0; f < filters; ++f){ + int index = b*filters*spatial + f*spatial + i; + x[index] /= sum; + dx[index] = (1 - x[index]) / sum; + } + } + } +} + + void normalize_cpu(float *x, float *mean, float *variance, int batch, int filters, int spatial) { int b, f, i; @@ -161,12 +193,48 @@ void fill_cpu(int N, float ALPHA, float *X, int INCX) for(i = 0; i < N; ++i) X[i*INCX] = ALPHA; } +void deinter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + int i, j; + int index = 0; + for(j = 0; j < B; ++j) { + for(i = 0; i < NX; ++i){ + if(X) X[j*NX + i] += OUT[index]; + ++index; + } + for(i = 0; i < NY; ++i){ + if(Y) Y[j*NY + i] += OUT[index]; + ++index; + } + } +} + +void inter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + int i, j; + int index = 0; + for(j = 0; j < B; ++j) { + for(i = 0; i < NX; ++i){ + OUT[index++] = X[j*NX + i]; + } + for(i = 0; i < NY; ++i){ + OUT[index++] = Y[j*NY + i]; + } + } +} + void copy_cpu(int N, float *X, int INCX, float *Y, int INCY) { int i; for(i = 0; i < N; ++i) Y[i*INCY] = X[i*INCX]; } +void mult_add_into_cpu(int N, float *X, float *Y, float *Z) +{ + int i; + for(i = 0; i < N; ++i) Z[i] += X[i]*Y[i]; +} + void smooth_l1_cpu(int n, float *pred, float *truth, float *delta, float *error) { int i; @@ -179,11 +247,43 @@ void smooth_l1_cpu(int n, float *pred, float *truth, float *delta, float *error) } else { error[i] = 2*abs_val - 1; - delta[i] = (diff < 0) ? -1 : 1; + delta[i] = (diff < 0) ? 1 : -1; } } } +void l1_cpu(int n, float *pred, float *truth, float *delta, float *error) +{ + int i; + for(i = 0; i < n; ++i){ + float diff = truth[i] - pred[i]; + error[i] = fabs(diff); + delta[i] = diff > 0 ? 1 : -1; + } +} + +void softmax_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error) +{ + int i; + for(i = 0; i < n; ++i){ + float t = truth[i]; + float p = pred[i]; + error[i] = (t) ? -log(p) : 0; + delta[i] = t-p; + } +} + +void logistic_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error) +{ + int i; + for(i = 0; i < n; ++i){ + float t = truth[i]; + float p = pred[i]; + error[i] = -t*log(p) - (1-t)*log(1-p); + delta[i] = t-p; + } +} + void l2_cpu(int n, float *pred, float *truth, float *delta, float *error) { int i; @@ -202,21 +302,50 @@ float dot_cpu(int N, float *X, int INCX, float *Y, int INCY) return dot; } -void softmax(float *input, int n, float temp, float *output) +void softmax(float *input, int n, float temp, int stride, float *output) { int i; float sum = 0; float largest = -FLT_MAX; for(i = 0; i < n; ++i){ - if(input[i] > largest) largest = input[i]; + if(input[i*stride] > largest) largest = input[i*stride]; } for(i = 0; i < n; ++i){ - float e = exp(input[i]/temp - largest/temp); + float e = exp(input[i*stride]/temp - largest/temp); sum += e; - output[i] = e; + output[i*stride] = e; } for(i = 0; i < n; ++i){ - output[i] /= sum; + output[i*stride] /= sum; } } + +void softmax_cpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output) +{ + int g, b; + for(b = 0; b < batch; ++b){ + for(g = 0; g < groups; ++g){ + softmax(input + b*batch_offset + g*group_offset, n, temp, stride, output + b*batch_offset + g*group_offset); + } + } +} + +void upsample_cpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out) +{ + int i, j, k, b; + for(b = 0; b < batch; ++b){ + for(k = 0; k < c; ++k){ + for(j = 0; j < h*stride; ++j){ + for(i = 0; i < w*stride; ++i){ + int in_index = b*w*h*c + k*w*h + (j/stride)*w + i/stride; + int out_index = b*w*h*c*stride*stride + k*w*h*stride*stride + j*w*stride + i; + if(forward) out[out_index] = scale*in[in_index]; + else in[in_index] += scale*out[out_index]; + } + } + } + } +} + + diff --git a/image.darknet/inst/include/darknet/src/blas.h b/image.darknet/inst/include/darknet/src/blas.h index 3d6ee7d..707291d 100644 --- a/image.darknet/inst/include/darknet/src/blas.h +++ b/image.darknet/inst/include/darknet/src/blas.h @@ -1,5 +1,7 @@ #ifndef BLAS_H #define BLAS_H +#include "darknet.h" + void flatten(float *x, int size, int layers, int batch, int forward); void pm(int M, int N, float *A); float *random_matrix(int rows, int cols); @@ -8,53 +10,60 @@ void reorg_cpu(float *x, int w, int h, int c, int batch, int stride, int forward void test_blas(); +void inter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT); +void deinter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT); +void mult_add_into_cpu(int N, float *X, float *Y, float *Z); + void const_cpu(int N, float ALPHA, float *X, int INCX); -void constrain_ongpu(int N, float ALPHA, float * X, int INCX); +void constrain_gpu(int N, float ALPHA, float * X, int INCX); void pow_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); void mul_cpu(int N, float *X, int INCX, float *Y, int INCY); -void axpy_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); -void copy_cpu(int N, float *X, int INCX, float *Y, int INCY); -void scal_cpu(int N, float ALPHA, float *X, int INCX); -void fill_cpu(int N, float ALPHA, float * X, int INCX); -float dot_cpu(int N, float *X, int INCX, float *Y, int INCY); -void test_gpu_blas(); -void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out); +int test_gpu_blas(); +void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out); void mean_cpu(float *x, int batch, int filters, int spatial, float *mean); void variance_cpu(float *x, float *mean, int batch, int filters, int spatial, float *variance); -void normalize_cpu(float *x, float *mean, float *variance, int batch, int filters, int spatial); void scale_bias(float *output, float *scales, int batch, int n, int size); void backward_scale_cpu(float *x_norm, float *delta, int batch, int n, int size, float *scale_updates); void mean_delta_cpu(float *delta, float *variance, int batch, int filters, int spatial, float *mean_delta); void variance_delta_cpu(float *x, float *delta, float *mean, float *variance, int batch, int filters, int spatial, float *variance_delta); void normalize_delta_cpu(float *x, float *mean, float *variance, float *mean_delta, float *variance_delta, int batch, int filters, int spatial, float *delta); +void l2normalize_cpu(float *x, float *dx, int batch, int filters, int spatial); void smooth_l1_cpu(int n, float *pred, float *truth, float *delta, float *error); void l2_cpu(int n, float *pred, float *truth, float *delta, float *error); +void l1_cpu(int n, float *pred, float *truth, float *delta, float *error); +void logistic_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error); +void softmax_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error); void weighted_sum_cpu(float *a, float *b, float *s, int num, float *c); +void weighted_delta_cpu(float *a, float *b, float *s, float *da, float *db, float *ds, int n, float *dc); -void softmax(float *input, int n, float temp, float *output); +void softmax(float *input, int n, float temp, int stride, float *output); +void softmax_cpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output); +void upsample_cpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out); #ifdef GPU #include "cuda.h" - -void axpy_ongpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY); -void axpy_ongpu_offset(int N, float ALPHA, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY); -void copy_ongpu(int N, float * X, int INCX, float * Y, int INCY); -void copy_ongpu_offset(int N, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY); -void scal_ongpu(int N, float ALPHA, float * X, int INCX); -void supp_ongpu(int N, float ALPHA, float * X, int INCX); -void mask_ongpu(int N, float * X, float mask_num, float * mask); -void const_ongpu(int N, float ALPHA, float *X, int INCX); -void pow_ongpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); -void mul_ongpu(int N, float *X, int INCX, float *Y, int INCY); -void fill_ongpu(int N, float ALPHA, float * X, int INCX); +#include "tree.h" + +void axpy_gpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY); +void axpy_gpu_offset(int N, float ALPHA, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY); +void copy_gpu(int N, float * X, int INCX, float * Y, int INCY); +void copy_gpu_offset(int N, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY); +void add_gpu(int N, float ALPHA, float * X, int INCX); +void supp_gpu(int N, float ALPHA, float * X, int INCX); +void mask_gpu(int N, float * X, float mask_num, float * mask, float val); +void scale_mask_gpu(int N, float * X, float mask_num, float * mask, float scale); +void const_gpu(int N, float ALPHA, float *X, int INCX); +void pow_gpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); +void mul_gpu(int N, float *X, int INCX, float *Y, int INCY); void mean_gpu(float *x, int batch, int filters, int spatial, float *mean); void variance_gpu(float *x, float *mean, int batch, int filters, int spatial, float *variance); void normalize_gpu(float *x, float *mean, float *variance, int batch, int filters, int spatial); +void l2normalize_gpu(float *x, float *dx, int batch, int filters, int spatial); void normalize_delta_gpu(float *x, float *mean, float *variance, float *mean_delta, float *variance_delta, int batch, int filters, int spatial, float *delta); @@ -63,25 +72,34 @@ void fast_variance_delta_gpu(float *x, float *delta, float *mean, float *varianc void fast_variance_gpu(float *x, float *mean, int batch, int filters, int spatial, float *variance); void fast_mean_gpu(float *x, int batch, int filters, int spatial, float *mean); -void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out); +void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out); void scale_bias_gpu(float *output, float *biases, int batch, int n, int size); void backward_scale_gpu(float *x_norm, float *delta, int batch, int n, int size, float *scale_updates); void scale_bias_gpu(float *output, float *biases, int batch, int n, int size); void add_bias_gpu(float *output, float *biases, int batch, int n, int size); void backward_bias_gpu(float *bias_updates, float *delta, int batch, int n, int size); +void logistic_x_ent_gpu(int n, float *pred, float *truth, float *delta, float *error); +void softmax_x_ent_gpu(int n, float *pred, float *truth, float *delta, float *error); void smooth_l1_gpu(int n, float *pred, float *truth, float *delta, float *error); void l2_gpu(int n, float *pred, float *truth, float *delta, float *error); +void l1_gpu(int n, float *pred, float *truth, float *delta, float *error); +void wgan_gpu(int n, float *pred, float *truth, float *delta, float *error); void weighted_delta_gpu(float *a, float *b, float *s, float *da, float *db, float *ds, int num, float *dc); void weighted_sum_gpu(float *a, float *b, float *s, int num, float *c); void mult_add_into_gpu(int num, float *a, float *b, float *c); +void inter_gpu(int NX, float *X, int NY, float *Y, int B, float *OUT); +void deinter_gpu(int NX, float *X, int NY, float *Y, int B, float *OUT); -void reorg_ongpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out); +void reorg_gpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out); -void softmax_gpu(float *input, int n, int offset, int groups, float temp, float *output); +void softmax_gpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output); +void adam_update_gpu(float *w, float *d, float *m, float *v, float B1, float B2, float eps, float decay, float rate, int n, int batch, int t); void adam_gpu(int n, float *x, float *m, float *v, float B1, float B2, float rate, float eps, int t); -void flatten_ongpu(float *x, int spatial, int layers, int batch, int forward, float *out); +void flatten_gpu(float *x, int spatial, int layers, int batch, int forward, float *out); +void softmax_tree(float *input, int spatial, int batch, int stride, float temp, float *output, tree hier); +void upsample_gpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/blas_kernels.cu b/image.darknet/inst/include/darknet/src/blas_kernels.cu index d940176..47e8217 100644 --- a/image.darknet/inst/include/darknet/src/blas_kernels.cu +++ b/image.darknet/inst/include/darknet/src/blas_kernels.cu @@ -53,24 +53,40 @@ void backward_scale_gpu(float *x_norm, float *delta, int batch, int n, int size, check_error(cudaPeekAtLastError()); } -__global__ void add_bias_kernel(float *output, float *biases, int n, int size) +__global__ void add_bias_kernel(float *output, float *biases, int batch, int n, int size) { - int offset = blockIdx.x * blockDim.x + threadIdx.x; - int filter = blockIdx.y; - int batch = blockIdx.z; + int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (index >= n*size*batch) return; + int i = index % size; + index /= size; + int j = index % n; + index /= n; + int k = index; - if(offset < size) output[(batch*n+filter)*size + offset] += biases[filter]; + output[(k*n+j)*size + i] += biases[j]; } void add_bias_gpu(float *output, float *biases, int batch, int n, int size) { - dim3 dimGrid((size-1)/BLOCK + 1, n, batch); - dim3 dimBlock(BLOCK, 1, 1); + int num = n*size*batch; - add_bias_kernel<<>>(output, biases, n, size); + add_bias_kernel<<>>(output, biases, batch, n, size); check_error(cudaPeekAtLastError()); } +__global__ void backward_bias_conn_kernel(float *bias_updates, float *delta, int batch, int n) +{ + int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (index >= n) return; + int b; + float sum = 0; + for(b = 0; b < batch; ++b){ + int i = b*n + index; + sum += delta[i]; + } + bias_updates[index] += sum; +} + __global__ void backward_bias_kernel(float *bias_updates, float *delta, int batch, int n, int size) { __shared__ float part[BLOCK]; @@ -91,6 +107,16 @@ __global__ void backward_bias_kernel(float *bias_updates, float *delta, int batc } } +void backward_bias_gpu(float *bias_updates, float *delta, int batch, int n, int size) +{ + if(size == 1){ + backward_bias_conn_kernel<<>>(bias_updates, delta, batch, n); + }else{ + backward_bias_kernel<<>>(bias_updates, delta, batch, n, size); + } + check_error(cudaPeekAtLastError()); +} + /* __global__ void dot_kernel(float *output, float scale, int batch, int n, int size, float *delta) { @@ -133,20 +159,16 @@ void dot_error_gpu(layer l) } */ -void backward_bias_gpu(float *bias_updates, float *delta, int batch, int n, int size) -{ - backward_bias_kernel<<>>(bias_updates, delta, batch, n, size); - check_error(cudaPeekAtLastError()); -} - __global__ void adam_kernel(int N, float *x, float *m, float *v, float B1, float B2, float rate, float eps, int t) { int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (index >= N) return; + + float mhat = m[index] / (1.f - powf(B1, t)); + float vhat = v[index] / (1.f - powf(B2, t)); - x[index] = x[index] - (rate * sqrt(1.-pow(B2, t)) / (1.-pow(B1, t)) * m[index] / (sqrt(v[index]) + eps)); - //if(index == 0) printf("%f %f %f %f\n", m[index], v[index], (rate * sqrt(1.-pow(B2, t)) / (1.-pow(B1, t)) * m[index] / (sqrt(v[index]) + eps))); + x[index] = x[index] + rate * mhat / (sqrtf(vhat) + eps); } extern "C" void adam_gpu(int n, float *x, float *m, float *v, float B1, float B2, float rate, float eps, int t) @@ -155,13 +177,27 @@ extern "C" void adam_gpu(int n, float *x, float *m, float *v, float B1, float B2 check_error(cudaPeekAtLastError()); } +extern "C" void adam_update_gpu(float *w, float *d, float *m, float *v, float B1, float B2, float eps, float decay, float rate, int n, int batch, int t) +{ + scal_gpu(n, B1, m, 1); + scal_gpu(n, B2, v, 1); + axpy_gpu(n, -decay*batch, w, 1, d, 1); + + axpy_gpu(n, (1-B1), d, 1, m, 1); + mul_gpu(n, d, 1, d, 1); + axpy_gpu(n, (1-B2), d, 1, v, 1); + + adam_gpu(n, w, m, v, B1, B2, rate, eps, t); + fill_gpu(n, 0, d, 1); +} + __global__ void normalize_kernel(int N, float *x, float *mean, float *variance, int batch, int filters, int spatial) { int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (index >= N) return; int f = (index/spatial)%filters; - x[index] = (x[index] - mean[f])/(sqrt(variance[f]) + .000001f); + x[index] = (x[index] - mean[f])/(sqrtf(variance[f] + .00001f)); } __global__ void normalize_delta_kernel(int N, float *x, float *mean, float *variance, float *mean_delta, float *variance_delta, int batch, int filters, int spatial, float *delta) @@ -170,7 +206,7 @@ __global__ void normalize_delta_kernel(int N, float *x, float *mean, float *vari if (index >= N) return; int f = (index/spatial)%filters; - delta[index] = delta[index] * 1./(sqrt(variance[f]) + .000001f) + variance_delta[f] * 2. * (x[index] - mean[f]) / (spatial * batch) + mean_delta[f]/(spatial*batch); + delta[index] = delta[index] * 1.f/(sqrtf(variance[f] + .00001f)) + variance_delta[f] * 2.f * (x[index] - mean[f]) / (spatial * batch) + mean_delta[f]/(spatial*batch); } extern "C" void normalize_delta_gpu(float *x, float *mean, float *variance, float *mean_delta, float *variance_delta, int batch, int filters, int spatial, float *delta) @@ -192,7 +228,7 @@ __global__ void variance_delta_kernel(float *x, float *delta, float *mean, floa variance_delta[i] += delta[index]*(x[index] - mean[i]); } } - variance_delta[i] *= -.5 * pow(variance[i] + .000001f, (float)(-3./2.)); + variance_delta[i] *= -.5f * powf(variance[i] + .00001f, (float)(-3.f/2.f)); } __global__ void accumulate_kernel(float *x, int n, int groups, float *sum) @@ -224,12 +260,14 @@ __global__ void fast_mean_delta_kernel(float *delta, float *variance, int batch, } } + __syncthreads(); + if(id == 0){ mean_delta[filter] = 0; for(i = 0; i < threads; ++i){ mean_delta[filter] += local[i]; } - mean_delta[filter] *= (-1./sqrt(variance[filter] + .000001f)); + mean_delta[filter] *= (-1.f/sqrtf(variance[filter] + .00001f)); } } @@ -252,12 +290,14 @@ __global__ void fast_variance_delta_kernel(float *x, float *delta, float *mean, } } + __syncthreads(); + if(id == 0){ variance_delta[filter] = 0; for(i = 0; i < threads; ++i){ variance_delta[filter] += local[i]; } - variance_delta[filter] *= -.5 * pow(variance[filter] + .000001f, (float)(-3./2.)); + variance_delta[filter] *= -.5f * powf(variance[filter] + .00001f, (float)(-3.f/2.f)); } } @@ -274,7 +314,7 @@ __global__ void mean_delta_kernel(float *delta, float *variance, int batch, int mean_delta[i] += delta[index]; } } - mean_delta[i] *= (-1./sqrt(variance[i] + .000001f)); + mean_delta[i] *= (-1.f/sqrtf(variance[i] + .00001f)); } extern "C" void mean_delta_gpu(float *delta, float *variance, int batch, int filters, int spatial, float *mean_delta) @@ -297,7 +337,7 @@ extern "C" void fast_variance_delta_gpu(float *x, float *delta, float *mean, flo __global__ void mean_kernel(float *x, int batch, int filters, int spatial, float *mean) { - float scale = 1./(batch * spatial); + float scale = 1.f/(batch * spatial); int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (i >= filters) return; int j,k; @@ -313,7 +353,7 @@ __global__ void mean_kernel(float *x, int batch, int filters, int spatial, floa __global__ void variance_kernel(float *x, float *mean, int batch, int filters, int spatial, float *variance) { - float scale = 1./(batch * spatial - 1); + float scale = 1.f/(batch * spatial - 1); int j,k; int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (i >= filters) return; @@ -321,7 +361,7 @@ __global__ void variance_kernel(float *x, float *mean, int batch, int filters, i for(j = 0; j < batch; ++j){ for(k = 0; k < spatial; ++k){ int index = j*filters*spatial + i*spatial + k; - variance[i] += pow((x[index] - mean[i]), 2); + variance[i] += powf((x[index] - mean[i]), 2); } } variance[i] *= scale; @@ -391,22 +431,22 @@ __global__ void supp_kernel(int N, float ALPHA, float *X, int INCX) } } -__global__ void scal_kernel(int N, float ALPHA, float *X, int INCX) +__global__ void add_kernel(int N, float ALPHA, float *X, int INCX) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; - if(i < N) X[i*INCX] *= ALPHA; + if(i < N) X[i*INCX] += ALPHA; } -__global__ void fill_kernel(int N, float ALPHA, float *X, int INCX) +__global__ void scal_kernel(int N, float ALPHA, float *X, int INCX) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; - if(i < N) X[i*INCX] = ALPHA; + if(i < N) X[i*INCX] *= ALPHA; } -__global__ void mask_kernel(int n, float *x, float mask_num, float *mask) +__global__ void fill_kernel(int N, float ALPHA, float *X, int INCX) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; - if(i < n && mask[i] == mask_num) x[i] = mask_num; + if(i < N) X[i*INCX] = ALPHA; } __global__ void copy_kernel(int N, float *X, int OFFX, int INCX, float *Y, int OFFY, int INCY) @@ -429,6 +469,35 @@ extern "C" void normalize_gpu(float *x, float *mean, float *variance, int batch, check_error(cudaPeekAtLastError()); } +__global__ void l2norm_kernel(int N, float *x, float *dx, int batch, int filters, int spatial) +{ + int index = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (index >= N) return; + int b = index / spatial; + int i = index % spatial; + int f; + float sum = 0; + for(f = 0; f < filters; ++f){ + int index = b*filters*spatial + f*spatial + i; + sum += powf(x[index], 2); + } + sum = sqrtf(sum); + if(sum == 0) sum = 1; + //printf("%f\n", sum); + for(f = 0; f < filters; ++f){ + int index = b*filters*spatial + f*spatial + i; + x[index] /= sum; + dx[index] = (1 - x[index]) / sum; + } +} + +extern "C" void l2normalize_gpu(float *x, float *dx, int batch, int filters, int spatial) +{ + size_t N = batch*spatial; + l2norm_kernel<<>>(N, x, dx, batch, filters, spatial); + check_error(cudaPeekAtLastError()); +} + __global__ void fast_mean_kernel(float *x, int batch, int filters, int spatial, float *mean) { const int threads = BLOCK; @@ -447,6 +516,8 @@ __global__ void fast_mean_kernel(float *x, int batch, int filters, int spatial, } } + __syncthreads(); + if(id == 0){ mean[filter] = 0; for(i = 0; i < threads; ++i){ @@ -471,10 +542,12 @@ __global__ void fast_variance_kernel(float *x, float *mean, int batch, int filt for(i = 0; i < spatial; i += threads){ int index = j*spatial*filters + filter*spatial + i + id; - local[id] += (i+id < spatial) ? pow((x[index] - mean[filter]), 2) : 0; + local[id] += (i+id < spatial) ? powf((x[index] - mean[filter]), 2) : 0; } } + __syncthreads(); + if(id == 0){ variance[filter] = 0; for(i = 0; i < threads; ++i){ @@ -509,35 +582,35 @@ extern "C" void variance_gpu(float *x, float *mean, int batch, int filters, int check_error(cudaPeekAtLastError()); } -extern "C" void axpy_ongpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY) +extern "C" void axpy_gpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY) { - axpy_ongpu_offset(N, ALPHA, X, 0, INCX, Y, 0, INCY); + axpy_gpu_offset(N, ALPHA, X, 0, INCX, Y, 0, INCY); } -extern "C" void pow_ongpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY) +extern "C" void pow_gpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY) { pow_kernel<<>>(N, ALPHA, X, INCX, Y, INCY); check_error(cudaPeekAtLastError()); } -extern "C" void axpy_ongpu_offset(int N, float ALPHA, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY) +extern "C" void axpy_gpu_offset(int N, float ALPHA, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY) { axpy_kernel<<>>(N, ALPHA, X, OFFX, INCX, Y, OFFY, INCY); check_error(cudaPeekAtLastError()); } -extern "C" void copy_ongpu(int N, float * X, int INCX, float * Y, int INCY) +extern "C" void copy_gpu(int N, float * X, int INCX, float * Y, int INCY) { - copy_ongpu_offset(N, X, 0, INCX, Y, 0, INCY); + copy_gpu_offset(N, X, 0, INCX, Y, 0, INCY); } -extern "C" void mul_ongpu(int N, float * X, int INCX, float * Y, int INCY) +extern "C" void mul_gpu(int N, float * X, int INCX, float * Y, int INCY) { mul_kernel<<>>(N, X, INCX, Y, INCY); check_error(cudaPeekAtLastError()); } -extern "C" void copy_ongpu_offset(int N, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY) +extern "C" void copy_gpu_offset(int N, float * X, int OFFX, int INCX, float * Y, int OFFY, int INCY) { copy_kernel<<>>(N, X, OFFX, INCX, Y, OFFY, INCY); check_error(cudaPeekAtLastError()); @@ -560,58 +633,82 @@ __global__ void flatten_kernel(int N, float *x, int spatial, int layers, int bat else out[i1] = x[i2]; } -extern "C" void flatten_ongpu(float *x, int spatial, int layers, int batch, int forward, float *out) +extern "C" void flatten_gpu(float *x, int spatial, int layers, int batch, int forward, float *out) { int size = spatial*batch*layers; flatten_kernel<<>>(size, x, spatial, layers, batch, forward, out); check_error(cudaPeekAtLastError()); } -extern "C" void reorg_ongpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out) +extern "C" void reorg_gpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out) { int size = w*h*c*batch; reorg_kernel<<>>(size, x, w, h, c, batch, stride, forward, out); check_error(cudaPeekAtLastError()); } -extern "C" void mask_ongpu(int N, float * X, float mask_num, float * mask) +__global__ void mask_kernel(int n, float *x, float mask_num, float *mask, float val) { - mask_kernel<<>>(N, X, mask_num, mask); + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n && mask[i] == mask_num) x[i] = val; +} + +extern "C" void mask_gpu(int N, float * X, float mask_num, float * mask, float val) +{ + mask_kernel<<>>(N, X, mask_num, mask, val); + check_error(cudaPeekAtLastError()); +} + +__global__ void scale_mask_kernel(int n, float *x, float mask_num, float *mask, float scale) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n && mask[i] == mask_num) x[i] *= scale; +} + +extern "C" void scale_mask_gpu(int N, float * X, float mask_num, float * mask, float scale) +{ + scale_mask_kernel<<>>(N, X, mask_num, mask, scale); check_error(cudaPeekAtLastError()); } -extern "C" void const_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void const_gpu(int N, float ALPHA, float * X, int INCX) { const_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -extern "C" void constrain_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void constrain_gpu(int N, float ALPHA, float * X, int INCX) { constrain_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -extern "C" void scal_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void add_gpu(int N, float ALPHA, float * X, int INCX) +{ + add_kernel<<>>(N, ALPHA, X, INCX); + check_error(cudaPeekAtLastError()); +} + +extern "C" void scal_gpu(int N, float ALPHA, float * X, int INCX) { scal_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -extern "C" void supp_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void supp_gpu(int N, float ALPHA, float * X, int INCX) { supp_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -extern "C" void fill_ongpu(int N, float ALPHA, float * X, int INCX) +extern "C" void fill_gpu(int N, float ALPHA, float * X, int INCX) { fill_kernel<<>>(N, ALPHA, X, INCX); check_error(cudaPeekAtLastError()); } -__global__ void shortcut_kernel(int size, int minw, int minh, int minc, int stride, int sample, int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out) +__global__ void shortcut_kernel(int size, int minw, int minh, int minc, int stride, int sample, int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out) { int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if (id >= size) return; @@ -625,10 +722,11 @@ __global__ void shortcut_kernel(int size, int minw, int minh, int minc, int stri int out_index = i*sample + w2*(j*sample + h2*(k + c2*b)); int add_index = i*stride + w1*(j*stride + h1*(k + c1*b)); - out[out_index] += add[add_index]; + out[out_index] = s1*out[out_index] + s2*add[add_index]; + //out[out_index] += add[add_index]; } -extern "C" void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float *out) +extern "C" void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out) { int minw = (w1 < w2) ? w1 : w2; int minh = (h1 < h2) ? h1 : h2; @@ -642,7 +740,7 @@ extern "C" void shortcut_gpu(int batch, int w1, int h1, int c1, float *add, int if(sample < 1) sample = 1; int size = batch * minw * minh * minc; - shortcut_kernel<<>>(size, minw, minh, minc, stride, sample, batch, w1, h1, c1, add, w2, h2, c2, out); + shortcut_kernel<<>>(size, minw, minh, minc, stride, sample, batch, w1, h1, c1, add, w2, h2, c2, s1, s2, out); check_error(cudaPeekAtLastError()); } @@ -651,14 +749,14 @@ __global__ void smooth_l1_kernel(int n, float *pred, float *truth, float *delta, int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if(i < n){ float diff = truth[i] - pred[i]; - float abs_val = abs(diff); + float abs_val = fabsf(diff); if(abs_val < 1) { error[i] = diff * diff; delta[i] = diff; } else { error[i] = 2*abs_val - 1; - delta[i] = (diff < 0) ? -1 : 1; + delta[i] = (diff > 0) ? 1 : -1; } } } @@ -669,6 +767,40 @@ extern "C" void smooth_l1_gpu(int n, float *pred, float *truth, float *delta, fl check_error(cudaPeekAtLastError()); } +__global__ void softmax_x_ent_kernel(int n, float *pred, float *truth, float *delta, float *error) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n){ + float t = truth[i]; + float p = pred[i]; + error[i] = (t) ? -log(p) : 0; + delta[i] = t-p; + } +} + +extern "C" void softmax_x_ent_gpu(int n, float *pred, float *truth, float *delta, float *error) +{ + softmax_x_ent_kernel<<>>(n, pred, truth, delta, error); + check_error(cudaPeekAtLastError()); +} + +__global__ void logistic_x_ent_kernel(int n, float *pred, float *truth, float *delta, float *error) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n){ + float t = truth[i]; + float p = pred[i]; + error[i] = -t*log(p+.0000001) - (1-t)*log(1-p+.0000001); + delta[i] = t-p; + } +} + +extern "C" void logistic_x_ent_gpu(int n, float *pred, float *truth, float *delta, float *error) +{ + logistic_x_ent_kernel<<>>(n, pred, truth, delta, error); + check_error(cudaPeekAtLastError()); +} + __global__ void l2_kernel(int n, float *pred, float *truth, float *delta, float *error) { int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; @@ -685,6 +817,38 @@ extern "C" void l2_gpu(int n, float *pred, float *truth, float *delta, float *er check_error(cudaPeekAtLastError()); } +__global__ void l1_kernel(int n, float *pred, float *truth, float *delta, float *error) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n){ + float diff = truth[i] - pred[i]; + error[i] = abs(diff); + delta[i] = (diff > 0) ? 1 : -1; + } +} + +extern "C" void l1_gpu(int n, float *pred, float *truth, float *delta, float *error) +{ + l1_kernel<<>>(n, pred, truth, delta, error); + check_error(cudaPeekAtLastError()); +} + +__global__ void wgan_kernel(int n, float *pred, float *truth, float *delta, float *error) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < n){ + error[i] = truth[i] ? -pred[i] : pred[i]; + delta[i] = (truth[i] > 0) ? 1 : -1; + } +} + +extern "C" void wgan_gpu(int n, float *pred, float *truth, float *delta, float *error) +{ + wgan_kernel<<>>(n, pred, truth, delta, error); + check_error(cudaPeekAtLastError()); +} + + __global__ void weighted_sum_kernel(int n, float *a, float *b, float *s, float *c) @@ -695,6 +859,46 @@ __global__ void weighted_sum_kernel(int n, float *a, float *b, float *s, float * } } +__global__ void deinter_kernel(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < (NX+NY)*B){ + int b = i / (NX+NY); + int j = i % (NX+NY); + if (j < NX){ + if(X) X[b*NX + j] += OUT[i]; + } else { + if(Y) Y[b*NY + j - NX] += OUT[i]; + } + } +} + +extern "C" void deinter_gpu(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + deinter_kernel<<>>(NX, X, NY, Y, B, OUT); + check_error(cudaPeekAtLastError()); +} + +__global__ void inter_kernel(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i < (NX+NY)*B){ + int b = i / (NX+NY); + int j = i % (NX+NY); + if (j < NX){ + OUT[i] = X[b*NX + j]; + } else { + OUT[i] = Y[b*NY + j - NX]; + } + } +} + +extern "C" void inter_gpu(int NX, float *X, int NY, float *Y, int B, float *OUT) +{ + inter_kernel<<>>(NX, X, NY, Y, B, OUT); + check_error(cudaPeekAtLastError()); +} + extern "C" void weighted_sum_gpu(float *a, float *b, float *s, int num, float *c) { weighted_sum_kernel<<>>(num, a, b, s, c); @@ -706,8 +910,8 @@ __global__ void weighted_delta_kernel(int n, float *a, float *b, float *s, float int i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if(i < n){ if(da) da[i] += dc[i] * s[i]; - db[i] += dc[i] * (1-s[i]); - ds[i] += dc[i] * a[i] + dc[i] * -b[i]; + if(db) db[i] += dc[i] * (1-s[i]); + ds[i] += dc[i] * (a[i] - b[i]); } } @@ -732,36 +936,100 @@ extern "C" void mult_add_into_gpu(int num, float *a, float *b, float *c) } -__device__ void softmax_device(int n, float *input, float temp, float *output) +__device__ void softmax_device(float *input, int n, float temp, int stride, float *output) { int i; float sum = 0; float largest = -INFINITY; for(i = 0; i < n; ++i){ - int val = input[i]; + int val = input[i*stride]; largest = (val>largest) ? val : largest; } for(i = 0; i < n; ++i){ - float e = exp(input[i]/temp - largest/temp); + float e = expf(input[i*stride]/temp - largest/temp); sum += e; - output[i] = e; + output[i*stride] = e; } for(i = 0; i < n; ++i){ - output[i] /= sum; + output[i*stride] /= sum; } } -__global__ void softmax_kernel(int n, int offset, int batch, float *input, float temp, float *output) + +__global__ void softmax_tree_kernel(float *input, int spatial, int batch, int stride, float temp, float *output, int groups, int *group_size, int *group_offset) { - int b = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; - if(b >= batch) return; - softmax_device(n, input + b*offset, temp, output + b*offset); + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (id >= spatial*batch*groups) return; + int s = id % spatial; + id = id / spatial; + int g = id % groups; + int b = id / groups; + int goff = group_offset[g]*spatial; + int boff = b*stride; + softmax_device(input + goff + boff + s, group_size[g], temp, spatial, output + goff + boff + s); +} + +extern "C" void softmax_tree(float *input, int spatial, int batch, int stride, float temp, float *output, tree hier) +{ + int *tree_groups_size = cuda_make_int_array(hier.group_size, hier.groups); + int *tree_groups_offset = cuda_make_int_array(hier.group_offset, hier.groups); + /* + static int *tree_groups_size = 0; + static int *tree_groups_offset = 0; + if(!tree_groups_size){ + tree_groups_size = cuda_make_int_array(hier.group_size, hier.groups); + tree_groups_offset = cuda_make_int_array(hier.group_offset, hier.groups); + } + */ + int num = spatial*batch*hier.groups; + softmax_tree_kernel<<>>(input, spatial, batch, stride, temp, output, hier.groups, tree_groups_size, tree_groups_offset); + check_error(cudaPeekAtLastError()); + cuda_free((float *)tree_groups_size); + cuda_free((float *)tree_groups_offset); +} + +__global__ void softmax_kernel(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output) +{ + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if (id >= batch*groups) return; + int b = id / groups; + int g = id % groups; + softmax_device(input + b*batch_offset + g*group_offset, n, temp, stride, output + b*batch_offset + g*group_offset); } -extern "C" void softmax_gpu(float *input, int n, int offset, int groups, float temp, float *output) +extern "C" void softmax_gpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output) +{ + softmax_kernel<<>>(input, n, batch, batch_offset, groups, group_offset, stride, temp, output); + check_error(cudaPeekAtLastError()); +} + + +__global__ void upsample_kernel(size_t N, float *x, int w, int h, int c, int batch, int stride, int forward, float scale, float *out) +{ + size_t i = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(i >= N) return; + int out_index = i; + int out_w = i%(w*stride); + i = i/(w*stride); + int out_h = i%(h*stride); + i = i/(h*stride); + int out_c = i%c; + i = i/c; + int b = i%batch; + + int in_w = out_w / stride; + int in_h = out_h / stride; + int in_c = out_c; + + int in_index = b*w*h*c + in_c*w*h + in_h*w + in_w; + + + if(forward) out[out_index] += scale * x[in_index]; + else atomicAdd(x+in_index, scale * out[out_index]); +} +extern "C" void upsample_gpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out) { - int inputs = n; - int batch = groups; - softmax_kernel<<>>(inputs, offset, batch, input, temp, output); + size_t size = w*h*c*batch*stride*stride; + upsample_kernel<<>>(size, in, w, h, c, batch, stride, forward, scale, out); check_error(cudaPeekAtLastError()); } diff --git a/image.darknet/inst/include/darknet/src/box.c b/image.darknet/inst/include/darknet/src/box.c index 39dea06..8a1772c 100644 --- a/image.darknet/inst/include/darknet/src/box.c +++ b/image.darknet/inst/include/darknet/src/box.c @@ -3,13 +3,98 @@ #include #include -box float_to_box(float *f) +int nms_comparator(const void *pa, const void *pb) { - box b; + detection a = *(detection *)pa; + detection b = *(detection *)pb; + float diff = 0; + if(b.sort_class >= 0){ + diff = a.prob[b.sort_class] - b.prob[b.sort_class]; + } else { + diff = a.objectness - b.objectness; + } + if(diff < 0) return 1; + else if(diff > 0) return -1; + return 0; +} + +void do_nms_obj(detection *dets, int total, int classes, float thresh) +{ + int i, j, k; + k = total-1; + for(i = 0; i <= k; ++i){ + if(dets[i].objectness == 0){ + detection swap = dets[i]; + dets[i] = dets[k]; + dets[k] = swap; + --k; + --i; + } + } + total = k+1; + + for(i = 0; i < total; ++i){ + dets[i].sort_class = -1; + } + + qsort(dets, total, sizeof(detection), nms_comparator); + for(i = 0; i < total; ++i){ + if(dets[i].objectness == 0) continue; + box a = dets[i].bbox; + for(j = i+1; j < total; ++j){ + if(dets[j].objectness == 0) continue; + box b = dets[j].bbox; + if (box_iou(a, b) > thresh){ + dets[j].objectness = 0; + for(k = 0; k < classes; ++k){ + dets[j].prob[k] = 0; + } + } + } + } +} + + +void do_nms_sort(detection *dets, int total, int classes, float thresh) +{ + int i, j, k; + k = total-1; + for(i = 0; i <= k; ++i){ + if(dets[i].objectness == 0){ + detection swap = dets[i]; + dets[i] = dets[k]; + dets[k] = swap; + --k; + --i; + } + } + total = k+1; + + for(k = 0; k < classes; ++k){ + for(i = 0; i < total; ++i){ + dets[i].sort_class = k; + } + qsort(dets, total, sizeof(detection), nms_comparator); + for(i = 0; i < total; ++i){ + if(dets[i].prob[k] == 0) continue; + box a = dets[i].bbox; + for(j = i+1; j < total; ++j){ + box b = dets[j].bbox; + if (box_iou(a, b) > thresh){ + dets[j].prob[k] = 0; + } + } + } + } +} + +box float_to_box(float *f, int stride) +{ + box b = {0}; b.x = f[0]; - b.y = f[1]; - b.w = f[2]; - b.h = f[3]; + b.y = f[1*stride]; + b.w = f[2*stride]; + b.h = f[3*stride]; return b; } @@ -230,79 +315,6 @@ dbox diou(box a, box b) return dd; } -typedef struct{ - int index; - int class; - float **probs; -} sortable_bbox; - -int nms_comparator(const void *pa, const void *pb) -{ - sortable_bbox a = *(sortable_bbox *)pa; - sortable_bbox b = *(sortable_bbox *)pb; - float diff = a.probs[a.index][b.class] - b.probs[b.index][b.class]; - if(diff < 0) return 1; - else if(diff > 0) return -1; - return 0; -} - -void do_nms_obj(box *boxes, float **probs, int total, int classes, float thresh) -{ - int i, j, k; - sortable_bbox *s = calloc(total, sizeof(sortable_bbox)); - - for(i = 0; i < total; ++i){ - s[i].index = i; - s[i].class = classes; - s[i].probs = probs; - } - - qsort(s, total, sizeof(sortable_bbox), nms_comparator); - for(i = 0; i < total; ++i){ - if(probs[s[i].index][classes] == 0) continue; - box a = boxes[s[i].index]; - for(j = i+1; j < total; ++j){ - box b = boxes[s[j].index]; - if (box_iou(a, b) > thresh){ - for(k = 0; k < classes+1; ++k){ - probs[s[j].index][k] = 0; - } - } - } - } - free(s); -} - - -void do_nms_sort(box *boxes, float **probs, int total, int classes, float thresh) -{ - int i, j, k; - sortable_bbox *s = calloc(total, sizeof(sortable_bbox)); - - for(i = 0; i < total; ++i){ - s[i].index = i; - s[i].class = 0; - s[i].probs = probs; - } - - for(k = 0; k < classes; ++k){ - for(i = 0; i < total; ++i){ - s[i].class = k; - } - qsort(s, total, sizeof(sortable_bbox), nms_comparator); - for(i = 0; i < total; ++i){ - if(probs[s[i].index][k] == 0) continue; - box a = boxes[s[i].index]; - for(j = i+1; j < total; ++j){ - box b = boxes[s[j].index]; - if (box_iou(a, b) > thresh){ - probs[s[j].index][k] = 0; - } - } - } - } - free(s); -} void do_nms(box *boxes, float **probs, int total, int classes, float thresh) { diff --git a/image.darknet/inst/include/darknet/src/box.h b/image.darknet/inst/include/darknet/src/box.h index c65589b..dda3e59 100644 --- a/image.darknet/inst/include/darknet/src/box.h +++ b/image.darknet/inst/include/darknet/src/box.h @@ -1,21 +1,13 @@ #ifndef BOX_H #define BOX_H - -typedef struct{ - float x, y, w, h; -} box; +#include "darknet.h" typedef struct{ float dx, dy, dw, dh; } dbox; -box float_to_box(float *f); -float box_iou(box a, box b); float box_rmse(box a, box b); dbox diou(box a, box b); -void do_nms(box *boxes, float **probs, int total, int classes, float thresh); -void do_nms_sort(box *boxes, float **probs, int total, int classes, float thresh); -void do_nms_obj(box *boxes, float **probs, int total, int classes, float thresh); box decode_box(box b, box anchor); box encode_box(box b, box anchor); diff --git a/image.darknet/inst/include/darknet/src/classifier.h b/image.darknet/inst/include/darknet/src/classifier.h index 3c89f49..8b13789 100644 --- a/image.darknet/inst/include/darknet/src/classifier.h +++ b/image.darknet/inst/include/darknet/src/classifier.h @@ -1,2 +1 @@ -list *read_data_cfg(char *filename); diff --git a/image.darknet/inst/include/darknet/src/col2im.h b/image.darknet/inst/include/darknet/src/col2im.h index 0237497..3fbe053 100644 --- a/image.darknet/inst/include/darknet/src/col2im.h +++ b/image.darknet/inst/include/darknet/src/col2im.h @@ -6,7 +6,7 @@ void col2im_cpu(float* data_col, int ksize, int stride, int pad, float* data_im); #ifdef GPU -void col2im_ongpu(float *data_col, +void col2im_gpu(float *data_col, int channels, int height, int width, int ksize, int stride, int pad, float *data_im); #endif diff --git a/image.darknet/inst/include/darknet/src/col2im_kernels.cu b/image.darknet/inst/include/darknet/src/col2im_kernels.cu index aed2df9..ba45e0f 100644 --- a/image.darknet/inst/include/darknet/src/col2im_kernels.cu +++ b/image.darknet/inst/include/darknet/src/col2im_kernels.cu @@ -41,7 +41,7 @@ __global__ void col2im_gpu_kernel(const int n, const float* data_col, } } -void col2im_ongpu(float *data_col, +void col2im_gpu(float *data_col, int channels, int height, int width, int ksize, int stride, int pad, float *data_im){ // We are going to launch channels * height_col * width_col kernels, each diff --git a/image.darknet/inst/include/darknet/src/compare.c b/image.darknet/inst/include/darknet/src/compare.c index 4fd266c..d2d2b3b 100644 --- a/image.darknet/inst/include/darknet/src/compare.c +++ b/image.darknet/inst/include/darknet/src/compare.c @@ -54,7 +54,7 @@ void train_compare(char *cfgfile, char *weightfile) float loss = train_network(net, train); if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; - printf("%.3f: %f, %f avg, %lf seconds, %d images\n", (float)*net.seen/N, loss, avg_loss, sec(clock()-time), *net.seen); + printf("%.3f: %f, %f avg, %lf seconds, %ld images\n", (float)*net.seen/N, loss, avg_loss, sec(clock()-time), *net.seen); free_data(train); if(i%100 == 0){ char buff[256]; diff --git a/image.darknet/inst/include/darknet/src/connected_layer.c b/image.darknet/inst/include/darknet/src/connected_layer.c index b678ed0..353f4e5 100644 --- a/image.darknet/inst/include/darknet/src/connected_layer.c +++ b/image.darknet/inst/include/darknet/src/connected_layer.c @@ -1,4 +1,5 @@ #include "connected_layer.h" +#include "convolutional_layer.h" #include "batchnorm_layer.h" #include "utils.h" #include "cuda.h" @@ -10,10 +11,11 @@ #include #include -connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVATION activation, int batch_normalize) +layer make_connected_layer(int batch, int inputs, int outputs, ACTIVATION activation, int batch_normalize, int adam) { int i; - connected_layer l = {0}; + layer l = {0}; + l.learning_rate_scale = 1; l.type = CONNECTED; l.inputs = inputs; @@ -50,6 +52,14 @@ connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVAT l.biases[i] = 0; } + if(adam){ + l.m = calloc(l.inputs*l.outputs, sizeof(float)); + l.v = calloc(l.inputs*l.outputs, sizeof(float)); + l.bias_m = calloc(l.outputs, sizeof(float)); + l.scale_m = calloc(l.outputs, sizeof(float)); + l.bias_v = calloc(l.outputs, sizeof(float)); + l.scale_v = calloc(l.outputs, sizeof(float)); + } if(batch_normalize){ l.scales = calloc(outputs, sizeof(float)); l.scale_updates = calloc(outputs, sizeof(float)); @@ -82,10 +92,16 @@ connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVAT l.output_gpu = cuda_make_array(l.output, outputs*batch); l.delta_gpu = cuda_make_array(l.delta, outputs*batch); - if(batch_normalize){ - l.scales_gpu = cuda_make_array(l.scales, outputs); - l.scale_updates_gpu = cuda_make_array(l.scale_updates, outputs); + if (adam) { + l.m_gpu = cuda_make_array(0, inputs*outputs); + l.v_gpu = cuda_make_array(0, inputs*outputs); + l.bias_m_gpu = cuda_make_array(0, outputs); + l.bias_v_gpu = cuda_make_array(0, outputs); + l.scale_m_gpu = cuda_make_array(0, outputs); + l.scale_v_gpu = cuda_make_array(0, outputs); + } + if(batch_normalize){ l.mean_gpu = cuda_make_array(l.mean, outputs); l.variance_gpu = cuda_make_array(l.variance, outputs); @@ -95,8 +111,17 @@ connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVAT l.mean_delta_gpu = cuda_make_array(l.mean, outputs); l.variance_delta_gpu = cuda_make_array(l.variance, outputs); + l.scales_gpu = cuda_make_array(l.scales, outputs); + l.scale_updates_gpu = cuda_make_array(l.scale_updates, outputs); + l.x_gpu = cuda_make_array(l.output, l.batch*outputs); l.x_norm_gpu = cuda_make_array(l.output, l.batch*outputs); +#ifdef CUDNN + cudnnCreateTensorDescriptor(&l.normTensorDesc); + cudnnCreateTensorDescriptor(&l.dstTensorDesc); + cudnnSetTensor4dDescriptor(l.dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l.batch, l.out_c, l.out_h, l.out_w); + cudnnSetTensor4dDescriptor(l.normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l.out_c, 1, 1); +#endif } #endif l.activation = activation; @@ -104,8 +129,12 @@ connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVAT return l; } -void update_connected_layer(connected_layer l, int batch, float learning_rate, float momentum, float decay) +void update_connected_layer(layer l, update_args a) { + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; axpy_cpu(l.outputs, learning_rate/batch, l.bias_updates, 1, l.biases, 1); scal_cpu(l.outputs, momentum, l.bias_updates, 1); @@ -119,63 +148,39 @@ void update_connected_layer(connected_layer l, int batch, float learning_rate, f scal_cpu(l.inputs*l.outputs, momentum, l.weight_updates, 1); } -void forward_connected_layer(connected_layer l, network_state state) +void forward_connected_layer(layer l, network net) { - int i; fill_cpu(l.outputs*l.batch, 0, l.output, 1); int m = l.batch; int k = l.inputs; int n = l.outputs; - float *a = state.input; + float *a = net.input; float *b = l.weights; float *c = l.output; gemm(0,1,m,n,k,1,a,k,b,k,1,c,n); if(l.batch_normalize){ - if(state.train){ - mean_cpu(l.output, l.batch, l.outputs, 1, l.mean); - variance_cpu(l.output, l.mean, l.batch, l.outputs, 1, l.variance); - - scal_cpu(l.outputs, .95, l.rolling_mean, 1); - axpy_cpu(l.outputs, .05, l.mean, 1, l.rolling_mean, 1); - scal_cpu(l.outputs, .95, l.rolling_variance, 1); - axpy_cpu(l.outputs, .05, l.variance, 1, l.rolling_variance, 1); - - copy_cpu(l.outputs*l.batch, l.output, 1, l.x, 1); - normalize_cpu(l.output, l.mean, l.variance, l.batch, l.outputs, 1); - copy_cpu(l.outputs*l.batch, l.output, 1, l.x_norm, 1); - } else { - normalize_cpu(l.output, l.rolling_mean, l.rolling_variance, l.batch, l.outputs, 1); - } - scale_bias(l.output, l.scales, l.batch, l.outputs, 1); - } - for(i = 0; i < l.batch; ++i){ - axpy_cpu(l.outputs, 1, l.biases, 1, l.output + i*l.outputs, 1); + forward_batchnorm_layer(l, net); + } else { + add_bias(l.output, l.biases, l.batch, l.outputs, 1); } activate_array(l.output, l.outputs*l.batch, l.activation); } -void backward_connected_layer(connected_layer l, network_state state) +void backward_connected_layer(layer l, network net) { - int i; gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); - for(i = 0; i < l.batch; ++i){ - axpy_cpu(l.outputs, 1, l.delta + i*l.outputs, 1, l.bias_updates, 1); - } - if(l.batch_normalize){ - backward_scale_cpu(l.x_norm, l.delta, l.batch, l.outputs, 1, l.scale_updates); - - scale_bias(l.delta, l.scales, l.batch, l.outputs, 1); - mean_delta_cpu(l.delta, l.variance, l.batch, l.outputs, 1, l.mean_delta); - variance_delta_cpu(l.x, l.delta, l.mean, l.variance, l.batch, l.outputs, 1, l.variance_delta); - normalize_delta_cpu(l.x, l.mean, l.variance, l.mean_delta, l.variance_delta, l.batch, l.outputs, 1, l.delta); + if(l.batch_normalize){ + backward_batchnorm_layer(l, net); + } else { + backward_bias(l.bias_updates, l.delta, l.batch, l.outputs, 1); } int m = l.outputs; int k = l.batch; int n = l.inputs; float *a = l.delta; - float *b = state.input; + float *b = net.input; float *c = l.weight_updates; gemm(1,0,m,n,k,1,a,m,b,n,1,c,n); @@ -185,7 +190,7 @@ void backward_connected_layer(connected_layer l, network_state state) a = l.delta; b = l.weights; - c = state.delta; + c = net.delta; if(c) gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); } @@ -213,11 +218,11 @@ void statistics_connected_layer(layer l) printf("Scales "); print_statistics(l.scales, l.outputs); /* - printf("Rolling Mean "); - print_statistics(l.rolling_mean, l.outputs); - printf("Rolling Variance "); - print_statistics(l.rolling_variance, l.outputs); - */ + printf("Rolling Mean "); + print_statistics(l.rolling_mean, l.outputs); + printf("Rolling Variance "); + print_statistics(l.rolling_variance, l.outputs); + */ } printf("Biases "); print_statistics(l.biases, l.outputs); @@ -227,7 +232,7 @@ void statistics_connected_layer(layer l) #ifdef GPU -void pull_connected_layer(connected_layer l) +void pull_connected_layer(layer l) { cuda_pull_array(l.weights_gpu, l.weights, l.inputs*l.outputs); cuda_pull_array(l.biases_gpu, l.biases, l.outputs); @@ -240,7 +245,7 @@ void pull_connected_layer(connected_layer l) } } -void push_connected_layer(connected_layer l) +void push_connected_layer(layer l) { cuda_push_array(l.weights_gpu, l.weights, l.inputs*l.outputs); cuda_push_array(l.biases_gpu, l.biases, l.outputs); @@ -253,62 +258,70 @@ void push_connected_layer(connected_layer l) } } -void update_connected_layer_gpu(connected_layer l, int batch, float learning_rate, float momentum, float decay) +void update_connected_layer_gpu(layer l, update_args a) { - axpy_ongpu(l.outputs, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); - scal_ongpu(l.outputs, momentum, l.bias_updates_gpu, 1); + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + if(a.adam){ + adam_update_gpu(l.weights_gpu, l.weight_updates_gpu, l.m_gpu, l.v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.inputs*l.outputs, batch, a.t); + adam_update_gpu(l.biases_gpu, l.bias_updates_gpu, l.bias_m_gpu, l.bias_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.outputs, batch, a.t); + if(l.scales_gpu){ + adam_update_gpu(l.scales_gpu, l.scale_updates_gpu, l.scale_m_gpu, l.scale_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.outputs, batch, a.t); + } + }else{ + axpy_gpu(l.outputs, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); + scal_gpu(l.outputs, momentum, l.bias_updates_gpu, 1); - if(l.batch_normalize){ - axpy_ongpu(l.outputs, learning_rate/batch, l.scale_updates_gpu, 1, l.scales_gpu, 1); - scal_ongpu(l.outputs, momentum, l.scale_updates_gpu, 1); - } + if(l.batch_normalize){ + axpy_gpu(l.outputs, learning_rate/batch, l.scale_updates_gpu, 1, l.scales_gpu, 1); + scal_gpu(l.outputs, momentum, l.scale_updates_gpu, 1); + } - axpy_ongpu(l.inputs*l.outputs, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); - axpy_ongpu(l.inputs*l.outputs, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); - scal_ongpu(l.inputs*l.outputs, momentum, l.weight_updates_gpu, 1); + axpy_gpu(l.inputs*l.outputs, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); + axpy_gpu(l.inputs*l.outputs, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); + scal_gpu(l.inputs*l.outputs, momentum, l.weight_updates_gpu, 1); + } } -void forward_connected_layer_gpu(connected_layer l, network_state state) +void forward_connected_layer_gpu(layer l, network net) { - int i; - fill_ongpu(l.outputs*l.batch, 0, l.output_gpu, 1); + fill_gpu(l.outputs*l.batch, 0, l.output_gpu, 1); int m = l.batch; int k = l.inputs; int n = l.outputs; - float * a = state.input; + float * a = net.input_gpu; float * b = l.weights_gpu; float * c = l.output_gpu; - gemm_ongpu(0,1,m,n,k,1,a,k,b,k,1,c,n); - if(l.batch_normalize){ - forward_batchnorm_layer_gpu(l, state); - } - for(i = 0; i < l.batch; ++i){ - axpy_ongpu(l.outputs, 1, l.biases_gpu, 1, l.output_gpu + i*l.outputs, 1); + gemm_gpu(0,1,m,n,k,1,a,k,b,k,1,c,n); + + if (l.batch_normalize) { + forward_batchnorm_layer_gpu(l, net); + } else { + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.outputs, 1); } - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); } -void backward_connected_layer_gpu(connected_layer l, network_state state) +void backward_connected_layer_gpu(layer l, network net) { - int i; - constrain_ongpu(l.outputs*l.batch, 1, l.delta_gpu, 1); - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); - for(i = 0; i < l.batch; ++i){ - axpy_ongpu(l.outputs, 1, l.delta_gpu + i*l.outputs, 1, l.bias_updates_gpu, 1); - } - + constrain_gpu(l.outputs*l.batch, 1, l.delta_gpu, 1); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); if(l.batch_normalize){ - backward_batchnorm_layer_gpu(l, state); + backward_batchnorm_layer_gpu(l, net); + } else { + backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.outputs, 1); } int m = l.outputs; int k = l.batch; int n = l.inputs; float * a = l.delta_gpu; - float * b = state.input; + float * b = net.input_gpu; float * c = l.weight_updates_gpu; - gemm_ongpu(1,0,m,n,k,1,a,m,b,n,1,c,n); + gemm_gpu(1,0,m,n,k,1,a,m,b,n,1,c,n); m = l.batch; k = l.outputs; @@ -316,8 +329,8 @@ void backward_connected_layer_gpu(connected_layer l, network_state state) a = l.delta_gpu; b = l.weights_gpu; - c = state.delta; + c = net.delta_gpu; - if(c) gemm_ongpu(0,0,m,n,k,1,a,k,b,n,1,c,n); + if(c) gemm_gpu(0,0,m,n,k,1,a,k,b,n,1,c,n); } #endif diff --git a/image.darknet/inst/include/darknet/src/connected_layer.h b/image.darknet/inst/include/darknet/src/connected_layer.h index 23797b1..6727a96 100644 --- a/image.darknet/inst/include/darknet/src/connected_layer.h +++ b/image.darknet/inst/include/darknet/src/connected_layer.h @@ -5,22 +5,18 @@ #include "layer.h" #include "network.h" -typedef layer connected_layer; +layer make_connected_layer(int batch, int inputs, int outputs, ACTIVATION activation, int batch_normalize, int adam); -connected_layer make_connected_layer(int batch, int inputs, int outputs, ACTIVATION activation, int batch_normalize); - -void forward_connected_layer(connected_layer layer, network_state state); -void backward_connected_layer(connected_layer layer, network_state state); -void update_connected_layer(connected_layer layer, int batch, float learning_rate, float momentum, float decay); -void denormalize_connected_layer(layer l); -void statistics_connected_layer(layer l); +void forward_connected_layer(layer l, network net); +void backward_connected_layer(layer l, network net); +void update_connected_layer(layer l, update_args a); #ifdef GPU -void forward_connected_layer_gpu(connected_layer layer, network_state state); -void backward_connected_layer_gpu(connected_layer layer, network_state state); -void update_connected_layer_gpu(connected_layer layer, int batch, float learning_rate, float momentum, float decay); -void push_connected_layer(connected_layer layer); -void pull_connected_layer(connected_layer layer); +void forward_connected_layer_gpu(layer l, network net); +void backward_connected_layer_gpu(layer l, network net); +void update_connected_layer_gpu(layer l, update_args a); +void push_connected_layer(layer l); +void pull_connected_layer(layer l); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/convolutional_kernels.cu b/image.darknet/inst/include/darknet/src/convolutional_kernels.cu index fcaea03..4a1047b 100644 --- a/image.darknet/inst/include/darknet/src/convolutional_kernels.cu +++ b/image.darknet/inst/include/darknet/src/convolutional_kernels.cu @@ -33,7 +33,7 @@ __global__ void binarize_input_kernel(float *input, int n, int size, float *bina int i = 0; float mean = 0; for(i = 0; i < n; ++i){ - mean += abs(input[i*size + s]); + mean += fabsf(input[i*size + s]); } mean = mean / n; for(i = 0; i < n; ++i){ @@ -55,7 +55,7 @@ __global__ void binarize_weights_kernel(float *weights, int n, int size, float * int i = 0; float mean = 0; for(i = 0; i < size; ++i){ - mean += abs(weights[f*size + i]); + mean += fabsf(weights[f*size + i]); } mean = mean / size; for(i = 0; i < size; ++i){ @@ -70,19 +70,19 @@ void binarize_weights_gpu(float *weights, int n, int size, float *binary) check_error(cudaPeekAtLastError()); } -void forward_convolutional_layer_gpu(convolutional_layer l, network_state state) +void forward_convolutional_layer_gpu(convolutional_layer l, network net) { - fill_ongpu(l.outputs*l.batch, 0, l.output_gpu, 1); + fill_gpu(l.outputs*l.batch, 0, l.output_gpu, 1); if(l.binary){ - binarize_weights_gpu(l.weights_gpu, l.n, l.c*l.size*l.size, l.binary_weights_gpu); + binarize_weights_gpu(l.weights_gpu, l.n, l.c/l.groups*l.size*l.size, l.binary_weights_gpu); swap_binary(&l); } if(l.xnor){ - binarize_weights_gpu(l.weights_gpu, l.n, l.c*l.size*l.size, l.binary_weights_gpu); + binarize_weights_gpu(l.weights_gpu, l.n, l.c/l.groups*l.size*l.size, l.binary_weights_gpu); swap_binary(&l); - binarize_gpu(state.input, l.c*l.h*l.w*l.batch, l.binary_input_gpu); - state.input = l.binary_input_gpu; + binarize_gpu(net.input_gpu, l.c*l.h*l.w*l.batch, l.binary_input_gpu); + net.input_gpu = l.binary_input_gpu; } #ifdef CUDNN @@ -90,74 +90,126 @@ void forward_convolutional_layer_gpu(convolutional_layer l, network_state state) cudnnConvolutionForward(cudnn_handle(), &one, l.srcTensorDesc, - state.input, + net.input_gpu, l.weightDesc, l.weights_gpu, l.convDesc, l.fw_algo, - state.workspace, + net.workspace, l.workspace_size, &one, l.dstTensorDesc, l.output_gpu); #else - int i; - int m = l.n; - int k = l.size*l.size*l.c; + int i, j; + int m = l.n/l.groups; + int k = l.size*l.size*l.c/l.groups; int n = l.out_w*l.out_h; for(i = 0; i < l.batch; ++i){ - im2col_ongpu(state.input + i*l.c*l.h*l.w, l.c, l.h, l.w, l.size, l.stride, l.pad, state.workspace); - float * a = l.weights_gpu; - float * b = state.workspace; - float * c = l.output_gpu; - gemm_ongpu(0,0,m,n,k,1.,a,k,b,n,1.,c+i*m*n,n); + for(j = 0; j < l.groups; ++j){ + float *a = l.weights_gpu + j*l.nweights/l.groups; + float *b = net.workspace; + float *c = l.output_gpu + (i*l.groups + j)*n*m; + float *im = net.input_gpu + (i*l.groups + j)*l.c/l.groups*l.h*l.w; + + if (l.size == 1){ + b = im; + } else { + im2col_gpu(im, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, b); + } + gemm_gpu(0,0,m,n,k,1,a,k,b,n,1,c,n); + } } #endif if (l.batch_normalize) { - forward_batchnorm_layer_gpu(l, state); + forward_batchnorm_layer_gpu(l, net); + } else { + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.n, l.out_w*l.out_h); } - add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.n, l.out_w*l.out_h); - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); //if(l.dot > 0) dot_error_gpu(l); if(l.binary || l.xnor) swap_binary(&l); } -void backward_convolutional_layer_gpu(convolutional_layer l, network_state state) +__global__ void smooth_kernel(float *x, int n, int w, int h, int c, int size, float rate, float *delta) +{ + int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; + if(id >= n) return; + + int j = id % w; + id /= w; + int i = id % h; + id /= h; + int k = id % c; + id /= c; + int b = id; + + int w_offset = -(size/2.f); + int h_offset = -(size/2.f); + + int out_index = j + w*(i + h*(k + c*b)); + int l, m; + for(l = 0; l < size; ++l){ + for(m = 0; m < size; ++m){ + int cur_h = h_offset + i + l; + int cur_w = w_offset + j + m; + int index = cur_w + w*(cur_h + h*(k + b*c)); + int valid = (cur_h >= 0 && cur_h < h && + cur_w >= 0 && cur_w < w); + delta[out_index] += valid ? rate*(x[index] - x[out_index]) : 0; + } + } +} + +extern "C" void smooth_layer(layer l, int size, float rate) { - //constrain_ongpu(l.outputs*l.batch, 1, l.delta_gpu, 1); - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + int h = l.out_h; + int w = l.out_w; + int c = l.out_c; + + size_t n = h*w*c*l.batch; + + smooth_kernel<<>>(l.output_gpu, n, l.w, l.h, l.c, size, rate, l.delta_gpu); + check_error(cudaPeekAtLastError()); +} + +void backward_convolutional_layer_gpu(convolutional_layer l, network net) +{ + if(l.smooth){ + smooth_layer(l, 5, l.smooth); + } + //constrain_gpu(l.outputs*l.batch, 1, l.delta_gpu, 1); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); - backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.n, l.out_w*l.out_h); if(l.batch_normalize){ - backward_batchnorm_layer_gpu(l, state); - //axpy_ongpu(l.outputs*l.batch, -state.net.decay, l.x_gpu, 1, l.delta_gpu, 1); + backward_batchnorm_layer_gpu(l, net); } else { - //axpy_ongpu(l.outputs*l.batch, -state.net.decay, l.output_gpu, 1, l.delta_gpu, 1); + backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.n, l.out_w*l.out_h); } - float *original_input = state.input; + float *original_input = net.input_gpu; - if(l.xnor) state.input = l.binary_input_gpu; + if(l.xnor) net.input_gpu = l.binary_input_gpu; #ifdef CUDNN float one = 1; cudnnConvolutionBackwardFilter(cudnn_handle(), &one, l.srcTensorDesc, - state.input, + net.input_gpu, l.ddstTensorDesc, l.delta_gpu, l.convDesc, l.bf_algo, - state.workspace, + net.workspace, l.workspace_size, &one, l.dweightDesc, l.weight_updates_gpu); - if(state.delta){ + if(net.delta_gpu){ if(l.binary || l.xnor) swap_binary(&l); cudnnConvolutionBackwardData(cudnn_handle(), &one, @@ -167,108 +219,111 @@ void backward_convolutional_layer_gpu(convolutional_layer l, network_state state l.delta_gpu, l.convDesc, l.bd_algo, - state.workspace, + net.workspace, l.workspace_size, &one, l.dsrcTensorDesc, - state.delta); + net.delta_gpu); if(l.binary || l.xnor) swap_binary(&l); - if(l.xnor) gradient_array_ongpu(original_input, l.batch*l.c*l.h*l.w, HARDTAN, state.delta); + if(l.xnor) gradient_array_gpu(original_input, l.batch*l.c*l.h*l.w, HARDTAN, net.delta_gpu); } #else - int m = l.n; - int n = l.size*l.size*l.c; + int m = l.n/l.groups; + int n = l.size*l.size*l.c/l.groups; int k = l.out_w*l.out_h; - int i; + int i, j; for(i = 0; i < l.batch; ++i){ - float * a = l.delta_gpu; - float * b = state.workspace; - float * c = l.weight_updates_gpu; - - im2col_ongpu(state.input + i*l.c*l.h*l.w, l.c, l.h, l.w, l.size, l.stride, l.pad, state.workspace); - gemm_ongpu(0,1,m,n,k,1,a + i*m*k,k,b,k,1,c,n); - - if(state.delta){ - if(l.binary || l.xnor) swap_binary(&l); - float * a = l.weights_gpu; - float * b = l.delta_gpu; - float * c = state.workspace; - - gemm_ongpu(1,0,n,k,m,1,a,n,b + i*k*m,k,0,c,k); - - col2im_ongpu(state.workspace, l.c, l.h, l.w, l.size, l.stride, l.pad, state.delta + i*l.c*l.h*l.w); - if(l.binary || l.xnor) { - swap_binary(&l); + for(j = 0; j < l.groups; ++j){ + float *a = l.delta_gpu + (i*l.groups + j)*m*k; + float *b = net.workspace; + float *c = l.weight_updates_gpu + j*l.nweights/l.groups; + + float *im = net.input_gpu+(i*l.groups + j)*l.c/l.groups*l.h*l.w; + float *imd = net.delta_gpu+(i*l.groups + j)*l.c/l.groups*l.h*l.w; + + im2col_gpu(im, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, b); + gemm_gpu(0,1,m,n,k,1,a,k,b,k,1,c,n); + + if (net.delta_gpu) { + if (l.binary || l.xnor) swap_binary(&l); + a = l.weights_gpu + j*l.nweights/l.groups; + b = l.delta_gpu + (i*l.groups + j)*m*k; + c = net.workspace; + if (l.size == 1) { + c = imd; + } + + gemm_gpu(1,0,n,k,m,1,a,n,b,k,0,c,k); + + if (l.size != 1) { + col2im_gpu(net.workspace, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, imd); + } + if(l.binary || l.xnor) { + swap_binary(&l); + } } - if(l.xnor) gradient_array_ongpu(original_input + i*l.c*l.h*l.w, l.c*l.h*l.w, HARDTAN, state.delta + i*l.c*l.h*l.w); + if(l.xnor) gradient_array_gpu(original_input + i*l.c*l.h*l.w, l.c*l.h*l.w, HARDTAN, net.delta_gpu + i*l.c*l.h*l.w); } } #endif } -void pull_convolutional_layer(convolutional_layer layer) +void pull_convolutional_layer(layer l) { - cuda_pull_array(layer.weights_gpu, layer.weights, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.biases_gpu, layer.biases, layer.n); - cuda_pull_array(layer.weight_updates_gpu, layer.weight_updates, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.bias_updates_gpu, layer.bias_updates, layer.n); - if (layer.batch_normalize){ - cuda_pull_array(layer.scales_gpu, layer.scales, layer.n); - cuda_pull_array(layer.rolling_mean_gpu, layer.rolling_mean, layer.n); - cuda_pull_array(layer.rolling_variance_gpu, layer.rolling_variance, layer.n); - } - if (layer.adam){ - cuda_pull_array(layer.m_gpu, layer.m, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.v_gpu, layer.v, layer.c*layer.n*layer.size*layer.size); + cuda_pull_array(l.weights_gpu, l.weights, l.nweights); + cuda_pull_array(l.biases_gpu, l.biases, l.n); + cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.nweights); + cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.n); + if (l.batch_normalize){ + cuda_pull_array(l.scales_gpu, l.scales, l.n); + cuda_pull_array(l.rolling_mean_gpu, l.rolling_mean, l.n); + cuda_pull_array(l.rolling_variance_gpu, l.rolling_variance, l.n); } } -void push_convolutional_layer(convolutional_layer layer) +void push_convolutional_layer(layer l) { - cuda_push_array(layer.weights_gpu, layer.weights, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.biases_gpu, layer.biases, layer.n); - cuda_push_array(layer.weight_updates_gpu, layer.weight_updates, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.bias_updates_gpu, layer.bias_updates, layer.n); - if (layer.batch_normalize){ - cuda_push_array(layer.scales_gpu, layer.scales, layer.n); - cuda_push_array(layer.rolling_mean_gpu, layer.rolling_mean, layer.n); - cuda_push_array(layer.rolling_variance_gpu, layer.rolling_variance, layer.n); - } - if (layer.adam){ - cuda_push_array(layer.m_gpu, layer.m, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.v_gpu, layer.v, layer.c*layer.n*layer.size*layer.size); + cuda_push_array(l.weights_gpu, l.weights, l.nweights); + cuda_push_array(l.biases_gpu, l.biases, l.n); + cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.nweights); + cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.n); + if (l.batch_normalize){ + cuda_push_array(l.scales_gpu, l.scales, l.n); + cuda_push_array(l.rolling_mean_gpu, l.rolling_mean, l.n); + cuda_push_array(l.rolling_variance_gpu, l.rolling_variance, l.n); } } -void update_convolutional_layer_gpu(convolutional_layer layer, int batch, float learning_rate, float momentum, float decay) +void update_convolutional_layer_gpu(layer l, update_args a) { - int size = layer.size*layer.size*layer.c*layer.n; - axpy_ongpu(layer.n, learning_rate/batch, layer.bias_updates_gpu, 1, layer.biases_gpu, 1); - scal_ongpu(layer.n, momentum, layer.bias_updates_gpu, 1); - - if(layer.scales_gpu){ - axpy_ongpu(layer.n, learning_rate/batch, layer.scale_updates_gpu, 1, layer.scales_gpu, 1); - scal_ongpu(layer.n, momentum, layer.scale_updates_gpu, 1); - } - - if(layer.adam){ - scal_ongpu(size, layer.B1, layer.m_gpu, 1); - scal_ongpu(size, layer.B2, layer.v_gpu, 1); - - axpy_ongpu(size, -decay*batch, layer.weights_gpu, 1, layer.weight_updates_gpu, 1); + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + + if(a.adam){ + adam_update_gpu(l.weights_gpu, l.weight_updates_gpu, l.m_gpu, l.v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.nweights, batch, a.t); + adam_update_gpu(l.biases_gpu, l.bias_updates_gpu, l.bias_m_gpu, l.bias_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.n, batch, a.t); + if(l.scales_gpu){ + adam_update_gpu(l.scales_gpu, l.scale_updates_gpu, l.scale_m_gpu, l.scale_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.n, batch, a.t); + } + }else{ + axpy_gpu(l.nweights, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); + axpy_gpu(l.nweights, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); + scal_gpu(l.nweights, momentum, l.weight_updates_gpu, 1); - axpy_ongpu(size, -(1-layer.B1), layer.weight_updates_gpu, 1, layer.m_gpu, 1); - mul_ongpu(size, layer.weight_updates_gpu, 1, layer.weight_updates_gpu, 1); - axpy_ongpu(size, (1-layer.B2), layer.weight_updates_gpu, 1, layer.v_gpu, 1); + axpy_gpu(l.n, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); + scal_gpu(l.n, momentum, l.bias_updates_gpu, 1); - adam_gpu(size, layer.weights_gpu, layer.m_gpu, layer.v_gpu, layer.B1, layer.B2, learning_rate/batch, layer.eps, layer.t+1); - fill_ongpu(size, 0, layer.weight_updates_gpu, 1); - }else{ - axpy_ongpu(size, -decay*batch, layer.weights_gpu, 1, layer.weight_updates_gpu, 1); - axpy_ongpu(size, learning_rate/batch, layer.weight_updates_gpu, 1, layer.weights_gpu, 1); - scal_ongpu(size, momentum, layer.weight_updates_gpu, 1); + if(l.scales_gpu){ + axpy_gpu(l.n, learning_rate/batch, l.scale_updates_gpu, 1, l.scales_gpu, 1); + scal_gpu(l.n, momentum, l.scale_updates_gpu, 1); + } + } + if(l.clip){ + constrain_gpu(l.nweights, l.clip, l.weights_gpu, 1); } } diff --git a/image.darknet/inst/include/darknet/src/convolutional_layer.c b/image.darknet/inst/include/darknet/src/convolutional_layer.c index 37211ab..1fb58b0 100644 --- a/image.darknet/inst/include/darknet/src/convolutional_layer.c +++ b/image.darknet/inst/include/darknet/src/convolutional_layer.c @@ -12,22 +12,17 @@ #include "xnor_layer.h" #endif -#ifndef AI2 -#define AI2 0 -void forward_xnor_layer(layer l, network_state state); -#endif - void swap_binary(convolutional_layer *l) { float *swap = l->weights; l->weights = l->binary_weights; l->binary_weights = swap; - #ifdef GPU +#ifdef GPU swap = l->weights_gpu; l->weights_gpu = l->binary_weights_gpu; l->binary_weights_gpu = swap; - #endif +#endif } void binarize_weights(float *weights, int n, int size, float *binary) @@ -80,23 +75,15 @@ int convolutional_out_width(convolutional_layer l) image get_convolutional_image(convolutional_layer l) { - int h,w,c; - h = convolutional_out_height(l); - w = convolutional_out_width(l); - c = l.n; - return float_to_image(w,h,c,l.output); + return float_to_image(l.out_w,l.out_h,l.out_c,l.output); } image get_convolutional_delta(convolutional_layer l) { - int h,w,c; - h = convolutional_out_height(l); - w = convolutional_out_width(l); - c = l.n; - return float_to_image(w,h,c,l.delta); + return float_to_image(l.out_w,l.out_h,l.out_c,l.delta); } -size_t get_workspace_size(layer l){ +static size_t get_workspace_size(layer l){ #ifdef CUDNN if(gpu_index >= 0){ size_t most = 0; @@ -127,8 +114,8 @@ size_t get_workspace_size(layer l){ if (s > most) most = s; return most; } - #endif - return (size_t)l.out_h*l.out_w*l.size*l.size*l.c*sizeof(float); +#endif + return (size_t)l.out_h*l.out_w*l.size*l.size*l.c/l.groups*sizeof(float); } #ifdef GPU @@ -137,46 +124,62 @@ void cudnn_convolutional_setup(layer *l) { cudnnSetTensor4dDescriptor(l->dsrcTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->c, l->h, l->w); cudnnSetTensor4dDescriptor(l->ddstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->out_c, l->out_h, l->out_w); - cudnnSetFilter4dDescriptor(l->dweightDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, l->n, l->c, l->size, l->size); cudnnSetTensor4dDescriptor(l->srcTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->c, l->h, l->w); cudnnSetTensor4dDescriptor(l->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->out_c, l->out_h, l->out_w); - cudnnSetFilter4dDescriptor(l->weightDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, l->n, l->c, l->size, l->size); + cudnnSetTensor4dDescriptor(l->normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l->out_c, 1, 1); + + cudnnSetFilter4dDescriptor(l->dweightDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, l->n, l->c/l->groups, l->size, l->size); + cudnnSetFilter4dDescriptor(l->weightDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, l->n, l->c/l->groups, l->size, l->size); + #if CUDNN_MAJOR >= 6 + cudnnSetConvolution2dDescriptor(l->convDesc, l->pad, l->pad, l->stride, l->stride, 1, 1, CUDNN_CROSS_CORRELATION, CUDNN_DATA_FLOAT); + #else cudnnSetConvolution2dDescriptor(l->convDesc, l->pad, l->pad, l->stride, l->stride, 1, 1, CUDNN_CROSS_CORRELATION); + #endif + + #if CUDNN_MAJOR >= 7 + cudnnSetConvolutionGroupCount(l->convDesc, l->groups); + #else + if(l->groups > 1){ + error("CUDNN < 7 doesn't support groups, please upgrade!"); + } + #endif + cudnnGetConvolutionForwardAlgorithm(cudnn_handle(), l->srcTensorDesc, l->weightDesc, l->convDesc, l->dstTensorDesc, - CUDNN_CONVOLUTION_FWD_PREFER_FASTEST, - 0, + CUDNN_CONVOLUTION_FWD_SPECIFY_WORKSPACE_LIMIT, + 2000000000, &l->fw_algo); cudnnGetConvolutionBackwardDataAlgorithm(cudnn_handle(), l->weightDesc, l->ddstTensorDesc, l->convDesc, l->dsrcTensorDesc, - CUDNN_CONVOLUTION_BWD_DATA_PREFER_FASTEST, - 0, + CUDNN_CONVOLUTION_BWD_DATA_SPECIFY_WORKSPACE_LIMIT, + 2000000000, &l->bd_algo); cudnnGetConvolutionBackwardFilterAlgorithm(cudnn_handle(), l->srcTensorDesc, l->ddstTensorDesc, l->convDesc, l->dweightDesc, - CUDNN_CONVOLUTION_BWD_FILTER_PREFER_FASTEST, - 0, + CUDNN_CONVOLUTION_BWD_FILTER_SPECIFY_WORKSPACE_LIMIT, + 2000000000, &l->bf_algo); } #endif #endif -convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int binary, int xnor, int adam) +convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int n, int groups, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int binary, int xnor, int adam) { int i; convolutional_layer l = {0}; l.type = CONVOLUTIONAL; + l.groups = groups; l.h = h; l.w = w; l.c = c; @@ -189,17 +192,23 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.pad = padding; l.batch_normalize = batch_normalize; - l.weights = calloc(c*n*size*size, sizeof(float)); - l.weight_updates = calloc(c*n*size*size, sizeof(float)); + l.weights = calloc(c/groups*n*size*size, sizeof(float)); + l.weight_updates = calloc(c/groups*n*size*size, sizeof(float)); l.biases = calloc(n, sizeof(float)); l.bias_updates = calloc(n, sizeof(float)); + l.nweights = c/groups*n*size*size; + l.nbiases = n; + // float scale = 1./sqrt(size*size*c); - float scale = sqrt(2./(size*size*c)); - for(i = 0; i < c*n*size*size; ++i) l.weights[i] = scale*rand_uniform(-1, 1); - int out_h = convolutional_out_height(l); + float scale = sqrt(2./(size*size*c/l.groups)); + //printf("convscale %f\n", scale); + //scale = .02; + //for(i = 0; i < c*n*size*size; ++i) l.weights[i] = scale*rand_uniform(-1, 1); + for(i = 0; i < l.nweights; ++i) l.weights[i] = scale*rand_normal(); int out_w = convolutional_out_width(l); + int out_h = convolutional_out_height(l); l.out_h = out_h; l.out_w = out_w; l.out_c = n; @@ -213,12 +222,12 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.backward = backward_convolutional_layer; l.update = update_convolutional_layer; if(binary){ - l.binary_weights = calloc(c*n*size*size, sizeof(float)); - l.cweights = calloc(c*n*size*size, sizeof(char)); + l.binary_weights = calloc(l.nweights, sizeof(float)); + l.cweights = calloc(l.nweights, sizeof(char)); l.scales = calloc(n, sizeof(float)); } if(xnor){ - l.binary_weights = calloc(c*n*size*size, sizeof(float)); + l.binary_weights = calloc(l.nweights, sizeof(float)); l.binary_input = calloc(l.inputs*l.batch, sizeof(float)); } @@ -241,9 +250,12 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.x_norm = calloc(l.batch*l.outputs, sizeof(float)); } if(adam){ - l.adam = 1; - l.m = calloc(c*n*size*size, sizeof(float)); - l.v = calloc(c*n*size*size, sizeof(float)); + l.m = calloc(l.nweights, sizeof(float)); + l.v = calloc(l.nweights, sizeof(float)); + l.bias_m = calloc(n, sizeof(float)); + l.scale_m = calloc(n, sizeof(float)); + l.bias_v = calloc(n, sizeof(float)); + l.scale_v = calloc(n, sizeof(float)); } #ifdef GPU @@ -253,12 +265,16 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int if(gpu_index >= 0){ if (adam) { - l.m_gpu = cuda_make_array(l.m, c*n*size*size); - l.v_gpu = cuda_make_array(l.v, c*n*size*size); + l.m_gpu = cuda_make_array(l.m, l.nweights); + l.v_gpu = cuda_make_array(l.v, l.nweights); + l.bias_m_gpu = cuda_make_array(l.bias_m, n); + l.bias_v_gpu = cuda_make_array(l.bias_v, n); + l.scale_m_gpu = cuda_make_array(l.scale_m, n); + l.scale_v_gpu = cuda_make_array(l.scale_v, n); } - l.weights_gpu = cuda_make_array(l.weights, c*n*size*size); - l.weight_updates_gpu = cuda_make_array(l.weight_updates, c*n*size*size); + l.weights_gpu = cuda_make_array(l.weights, l.nweights); + l.weight_updates_gpu = cuda_make_array(l.weight_updates, l.nweights); l.biases_gpu = cuda_make_array(l.biases, n); l.bias_updates_gpu = cuda_make_array(l.bias_updates, n); @@ -267,10 +283,10 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.output_gpu = cuda_make_array(l.output, l.batch*out_h*out_w*n); if(binary){ - l.binary_weights_gpu = cuda_make_array(l.weights, c*n*size*size); + l.binary_weights_gpu = cuda_make_array(l.weights, l.nweights); } if(xnor){ - l.binary_weights_gpu = cuda_make_array(l.weights, c*n*size*size); + l.binary_weights_gpu = cuda_make_array(l.weights, l.nweights); l.binary_input_gpu = cuda_make_array(0, l.inputs*l.batch); } @@ -291,6 +307,7 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.x_norm_gpu = cuda_make_array(l.output, l.batch*out_h*out_w*n); } #ifdef CUDNN + cudnnCreateTensorDescriptor(&l.normTensorDesc); cudnnCreateTensorDescriptor(&l.srcTensorDesc); cudnnCreateTensorDescriptor(&l.dstTensorDesc); cudnnCreateFilterDescriptor(&l.weightDesc); @@ -305,7 +322,7 @@ convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int l.workspace_size = get_workspace_size(l); l.activation = activation; - fprintf(stderr, "conv %5d %2d x%2d /%2d %4d x%4d x%4d -> %4d x%4d x%4d\n", n, size, size, stride, w, h, c, l.out_w, l.out_h, l.out_c); + fprintf(stderr, "conv %5d %2d x%2d /%2d %4d x%4d x%4d -> %4d x%4d x%4d %5.3f BFLOPs\n", n, size, size, stride, w, h, c, l.out_w, l.out_h, l.out_c, (2.0 * l.n * l.size*l.size*l.c/l.groups * l.out_h*l.out_w)/1000000000.); return l; } @@ -315,8 +332,8 @@ void denormalize_convolutional_layer(convolutional_layer l) int i, j; for(i = 0; i < l.n; ++i){ float scale = l.scales[i]/sqrt(l.rolling_variance[i] + .00001); - for(j = 0; j < l.c*l.size*l.size; ++j){ - l.weights[i*l.c*l.size*l.size + j] *= scale; + for(j = 0; j < l.c/l.groups*l.size*l.size; ++j){ + l.weights[i*l.c/l.groups*l.size*l.size + j] *= scale; } l.biases[i] -= l.rolling_mean[i] * scale; l.scales[i] = 1; @@ -325,6 +342,7 @@ void denormalize_convolutional_layer(convolutional_layer l) } } +/* void test_convolutional_layer() { convolutional_layer l = make_convolutional_layer(1, 5, 5, 3, 2, 5, 2, 1, LEAKY, 1, 0, 0, 0); @@ -344,10 +362,10 @@ void test_convolutional_layer() 3,3,3,3,3, 3,3,3,3,3, 3,3,3,3,3}; - network_state state = {0}; - state.input = data; - forward_convolutional_layer(l, state); + //net.input = data; + //forward_convolutional_layer(l); } +*/ void resize_convolutional_layer(convolutional_layer *l, int w, int h) { @@ -424,88 +442,106 @@ void backward_bias(float *bias_updates, float *delta, int batch, int n, int size } } -void forward_convolutional_layer(convolutional_layer l, network_state state) +void forward_convolutional_layer(convolutional_layer l, network net) { - int out_h = convolutional_out_height(l); - int out_w = convolutional_out_width(l); - int i; + int i, j; fill_cpu(l.outputs*l.batch, 0, l.output, 1); if(l.xnor){ - binarize_weights(l.weights, l.n, l.c*l.size*l.size, l.binary_weights); + binarize_weights(l.weights, l.n, l.c/l.groups*l.size*l.size, l.binary_weights); swap_binary(&l); - binarize_cpu(state.input, l.c*l.h*l.w*l.batch, l.binary_input); - state.input = l.binary_input; + binarize_cpu(net.input, l.c*l.h*l.w*l.batch, l.binary_input); + net.input = l.binary_input; } - int m = l.n; - int k = l.size*l.size*l.c; - int n = out_h*out_w; - - - float *a = l.weights; - float *b = state.workspace; - float *c = l.output; - + int m = l.n/l.groups; + int k = l.size*l.size*l.c/l.groups; + int n = l.out_w*l.out_h; for(i = 0; i < l.batch; ++i){ - im2col_cpu(state.input, l.c, l.h, l.w, - l.size, l.stride, l.pad, b); - gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); - c += n*m; - state.input += l.c*l.h*l.w; + for(j = 0; j < l.groups; ++j){ + float *a = l.weights + j*l.nweights/l.groups; + float *b = net.workspace; + float *c = l.output + (i*l.groups + j)*n*m; + float *im = net.input + (i*l.groups + j)*l.c/l.groups*l.h*l.w; + + if (l.size == 1) { + b = im; + } else { + im2col_cpu(im, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, b); + } + gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); + } } if(l.batch_normalize){ - forward_batchnorm_layer(l, state); + forward_batchnorm_layer(l, net); + } else { + add_bias(l.output, l.biases, l.batch, l.n, l.out_h*l.out_w); } - add_bias(l.output, l.biases, l.batch, l.n, out_h*out_w); - activate_array(l.output, m*n*l.batch, l.activation); + activate_array(l.output, l.outputs*l.batch, l.activation); if(l.binary || l.xnor) swap_binary(&l); } -void backward_convolutional_layer(convolutional_layer l, network_state state) +void backward_convolutional_layer(convolutional_layer l, network net) { - int i; - int m = l.n; - int n = l.size*l.size*l.c; - int k = convolutional_out_height(l)* - convolutional_out_width(l); + int i, j; + int m = l.n/l.groups; + int n = l.size*l.size*l.c/l.groups; + int k = l.out_w*l.out_h; - gradient_array(l.output, m*k*l.batch, l.activation, l.delta); - backward_bias(l.bias_updates, l.delta, l.batch, l.n, k); + gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); if(l.batch_normalize){ - backward_batchnorm_layer(l, state); + backward_batchnorm_layer(l, net); + } else { + backward_bias(l.bias_updates, l.delta, l.batch, l.n, k); } for(i = 0; i < l.batch; ++i){ - float *a = l.delta + i*m*k; - float *b = state.workspace; - float *c = l.weight_updates; - - float *im = state.input+i*l.c*l.h*l.w; + for(j = 0; j < l.groups; ++j){ + float *a = l.delta + (i*l.groups + j)*m*k; + float *b = net.workspace; + float *c = l.weight_updates + j*l.nweights/l.groups; + + float *im = net.input + (i*l.groups + j)*l.c/l.groups*l.h*l.w; + float *imd = net.delta + (i*l.groups + j)*l.c/l.groups*l.h*l.w; + + if(l.size == 1){ + b = im; + } else { + im2col_cpu(im, l.c/l.groups, l.h, l.w, + l.size, l.stride, l.pad, b); + } - im2col_cpu(im, l.c, l.h, l.w, - l.size, l.stride, l.pad, b); - gemm(0,1,m,n,k,1,a,k,b,k,1,c,n); + gemm(0,1,m,n,k,1,a,k,b,k,1,c,n); - if(state.delta){ - a = l.weights; - b = l.delta + i*m*k; - c = state.workspace; + if (net.delta) { + a = l.weights + j*l.nweights/l.groups; + b = l.delta + (i*l.groups + j)*m*k; + c = net.workspace; + if (l.size == 1) { + c = imd; + } - gemm(1,0,n,k,m,1,a,n,b,k,0,c,k); + gemm(1,0,n,k,m,1,a,n,b,k,0,c,k); - col2im_cpu(state.workspace, l.c, l.h, l.w, l.size, l.stride, l.pad, state.delta+i*l.c*l.h*l.w); + if (l.size != 1) { + col2im_cpu(net.workspace, l.c/l.groups, l.h, l.w, l.size, l.stride, l.pad, imd); + } + } } } } -void update_convolutional_layer(convolutional_layer l, int batch, float learning_rate, float momentum, float decay) +void update_convolutional_layer(convolutional_layer l, update_args a) { - int size = l.size*l.size*l.c*l.n; + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + axpy_cpu(l.n, learning_rate/batch, l.bias_updates, 1, l.biases, 1); scal_cpu(l.n, momentum, l.bias_updates, 1); @@ -514,9 +550,9 @@ void update_convolutional_layer(convolutional_layer l, int batch, float learning scal_cpu(l.n, momentum, l.scale_updates, 1); } - axpy_cpu(size, -decay*batch, l.weights, 1, l.weight_updates, 1); - axpy_cpu(size, learning_rate/batch, l.weight_updates, 1, l.weights, 1); - scal_cpu(size, momentum, l.weight_updates, 1); + axpy_cpu(l.nweights, -decay*batch, l.weights, 1, l.weight_updates, 1); + axpy_cpu(l.nweights, learning_rate/batch, l.weight_updates, 1, l.weights, 1); + scal_cpu(l.nweights, momentum, l.weight_updates, 1); } @@ -524,7 +560,7 @@ image get_convolutional_weight(convolutional_layer l, int i) { int h = l.size; int w = l.size; - int c = l.c; + int c = l.c/l.groups; return float_to_image(w,h,c,l.weights+i*h*w*c); } @@ -558,8 +594,14 @@ image *get_weights(convolutional_layer l) int i; for(i = 0; i < l.n; ++i){ weights[i] = copy_image(get_convolutional_weight(l, i)); - //normalize_image(weights[i]); + normalize_image(weights[i]); + /* + char buff[256]; + sprintf(buff, "filter%d", i); + save_image(weights[i], buff); + */ } + //error("hey"); return weights; } diff --git a/image.darknet/inst/include/darknet/src/convolutional_layer.h b/image.darknet/inst/include/darknet/src/convolutional_layer.h index 970aa10..6c261f5 100644 --- a/image.darknet/inst/include/darknet/src/convolutional_layer.h +++ b/image.darknet/inst/include/darknet/src/convolutional_layer.h @@ -10,31 +10,31 @@ typedef layer convolutional_layer; #ifdef GPU -void forward_convolutional_layer_gpu(convolutional_layer layer, network_state state); -void backward_convolutional_layer_gpu(convolutional_layer layer, network_state state); -void update_convolutional_layer_gpu(convolutional_layer layer, int batch, float learning_rate, float momentum, float decay); +void forward_convolutional_layer_gpu(convolutional_layer layer, network net); +void backward_convolutional_layer_gpu(convolutional_layer layer, network net); +void update_convolutional_layer_gpu(convolutional_layer layer, update_args a); void push_convolutional_layer(convolutional_layer layer); void pull_convolutional_layer(convolutional_layer layer); void add_bias_gpu(float *output, float *biases, int batch, int n, int size); void backward_bias_gpu(float *bias_updates, float *delta, int batch, int n, int size); +void adam_update_gpu(float *w, float *d, float *m, float *v, float B1, float B2, float eps, float decay, float rate, int n, int batch, int t); #ifdef CUDNN void cudnn_convolutional_setup(layer *l); #endif #endif -convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int binary, int xnor, int adam); -void denormalize_convolutional_layer(convolutional_layer l); +convolutional_layer make_convolutional_layer(int batch, int h, int w, int c, int n, int groups, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int binary, int xnor, int adam); void resize_convolutional_layer(convolutional_layer *layer, int w, int h); -void forward_convolutional_layer(const convolutional_layer layer, network_state state); -void update_convolutional_layer(convolutional_layer layer, int batch, float learning_rate, float momentum, float decay); +void forward_convolutional_layer(const convolutional_layer layer, network net); +void update_convolutional_layer(convolutional_layer layer, update_args a); image *visualize_convolutional_layer(convolutional_layer layer, char *window, image *prev_weights); void binarize_weights(float *weights, int n, int size, float *binary); void swap_binary(convolutional_layer *l); void binarize_weights2(float *weights, int n, int size, char *binary, float *scales); -void backward_convolutional_layer(convolutional_layer layer, network_state state); +void backward_convolutional_layer(convolutional_layer layer, network net); void add_bias(float *output, float *biases, int batch, int n, int size); void backward_bias(float *bias_updates, float *delta, int batch, int n, int size); @@ -45,8 +45,6 @@ image get_convolutional_weight(convolutional_layer layer, int i); int convolutional_out_height(convolutional_layer layer); int convolutional_out_width(convolutional_layer layer); -void rescale_weights(convolutional_layer l, float scale, float trans); -void rgbgr_weights(convolutional_layer l); #endif diff --git a/image.darknet/inst/include/darknet/src/cost_layer.c b/image.darknet/inst/include/darknet/src/cost_layer.c index 39d2398..2138ff2 100644 --- a/image.darknet/inst/include/darknet/src/cost_layer.c +++ b/image.darknet/inst/include/darknet/src/cost_layer.c @@ -9,9 +9,12 @@ COST_TYPE get_cost_type(char *s) { + if (strcmp(s, "seg")==0) return SEG; if (strcmp(s, "sse")==0) return SSE; if (strcmp(s, "masked")==0) return MASKED; if (strcmp(s, "smooth")==0) return SMOOTH; + if (strcmp(s, "L1")==0) return L1; + if (strcmp(s, "wgan")==0) return WGAN; fprintf(stderr, "Couldn't find cost type %s, going with SSE\n", s); return SSE; } @@ -19,12 +22,18 @@ COST_TYPE get_cost_type(char *s) char *get_cost_string(COST_TYPE a) { switch(a){ + case SEG: + return "seg"; case SSE: return "sse"; case MASKED: return "masked"; case SMOOTH: return "smooth"; + case L1: + return "L1"; + case WGAN: + return "wgan"; } return "sse"; } @@ -70,26 +79,28 @@ void resize_cost_layer(cost_layer *l, int inputs) #endif } -void forward_cost_layer(cost_layer l, network_state state) +void forward_cost_layer(cost_layer l, network net) { - if (!state.truth) return; + if (!net.truth) return; if(l.cost_type == MASKED){ int i; for(i = 0; i < l.batch*l.inputs; ++i){ - if(state.truth[i] == SECRET_NUM) state.input[i] = SECRET_NUM; + if(net.truth[i] == SECRET_NUM) net.input[i] = SECRET_NUM; } } if(l.cost_type == SMOOTH){ - smooth_l1_cpu(l.batch*l.inputs, state.input, state.truth, l.delta, l.output); + smooth_l1_cpu(l.batch*l.inputs, net.input, net.truth, l.delta, l.output); + }else if(l.cost_type == L1){ + l1_cpu(l.batch*l.inputs, net.input, net.truth, l.delta, l.output); } else { - l2_cpu(l.batch*l.inputs, state.input, state.truth, l.delta, l.output); + l2_cpu(l.batch*l.inputs, net.input, net.truth, l.delta, l.output); } l.cost[0] = sum_array(l.output, l.batch*l.inputs); } -void backward_cost_layer(const cost_layer l, network_state state) +void backward_cost_layer(const cost_layer l, network net) { - axpy_cpu(l.batch*l.inputs, l.scale, l.delta, 1, state.delta, 1); + axpy_cpu(l.batch*l.inputs, l.scale, l.delta, 1, net.delta, 1); } #ifdef GPU @@ -113,17 +124,30 @@ int float_abs_compare (const void * a, const void * b) return (fa > fb) - (fa < fb); } -void forward_cost_layer_gpu(cost_layer l, network_state state) +void forward_cost_layer_gpu(cost_layer l, network net) { - if (!state.truth) return; - if (l.cost_type == MASKED) { - mask_ongpu(l.batch*l.inputs, state.input, SECRET_NUM, state.truth); + if (!net.truth) return; + if(l.smooth){ + scal_gpu(l.batch*l.inputs, (1-l.smooth), net.truth_gpu, 1); + add_gpu(l.batch*l.inputs, l.smooth * 1./l.inputs, net.truth_gpu, 1); } if(l.cost_type == SMOOTH){ - smooth_l1_gpu(l.batch*l.inputs, state.input, state.truth, l.delta_gpu, l.output_gpu); + smooth_l1_gpu(l.batch*l.inputs, net.input_gpu, net.truth_gpu, l.delta_gpu, l.output_gpu); + } else if (l.cost_type == L1){ + l1_gpu(l.batch*l.inputs, net.input_gpu, net.truth_gpu, l.delta_gpu, l.output_gpu); + } else if (l.cost_type == WGAN){ + wgan_gpu(l.batch*l.inputs, net.input_gpu, net.truth_gpu, l.delta_gpu, l.output_gpu); } else { - l2_gpu(l.batch*l.inputs, state.input, state.truth, l.delta_gpu, l.output_gpu); + l2_gpu(l.batch*l.inputs, net.input_gpu, net.truth_gpu, l.delta_gpu, l.output_gpu); + } + + if (l.cost_type == SEG && l.noobject_scale != 1) { + scale_mask_gpu(l.batch*l.inputs, l.delta_gpu, 0, net.truth_gpu, l.noobject_scale); + scale_mask_gpu(l.batch*l.inputs, l.output_gpu, 0, net.truth_gpu, l.noobject_scale); + } + if (l.cost_type == MASKED) { + mask_gpu(l.batch*l.inputs, net.delta_gpu, SECRET_NUM, net.truth_gpu, 0); } if(l.ratio){ @@ -133,16 +157,20 @@ void forward_cost_layer_gpu(cost_layer l, network_state state) float thresh = l.delta[n]; thresh = 0; printf("%f\n", thresh); - supp_ongpu(l.batch*l.inputs, thresh, l.delta_gpu, 1); + supp_gpu(l.batch*l.inputs, thresh, l.delta_gpu, 1); + } + + if(l.thresh){ + supp_gpu(l.batch*l.inputs, l.thresh*1./l.inputs, l.delta_gpu, 1); } cuda_pull_array(l.output_gpu, l.output, l.batch*l.inputs); l.cost[0] = sum_array(l.output, l.batch*l.inputs); } -void backward_cost_layer_gpu(const cost_layer l, network_state state) +void backward_cost_layer_gpu(const cost_layer l, network net) { - axpy_ongpu(l.batch*l.inputs, l.scale, l.delta_gpu, 1, state.delta, 1); + axpy_gpu(l.batch*l.inputs, l.scale, l.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/inst/include/darknet/src/cost_layer.h b/image.darknet/inst/include/darknet/src/cost_layer.h index a692831..ceb64de 100644 --- a/image.darknet/inst/include/darknet/src/cost_layer.h +++ b/image.darknet/inst/include/darknet/src/cost_layer.h @@ -8,13 +8,13 @@ typedef layer cost_layer; COST_TYPE get_cost_type(char *s); char *get_cost_string(COST_TYPE a); cost_layer make_cost_layer(int batch, int inputs, COST_TYPE type, float scale); -void forward_cost_layer(const cost_layer l, network_state state); -void backward_cost_layer(const cost_layer l, network_state state); +void forward_cost_layer(const cost_layer l, network net); +void backward_cost_layer(const cost_layer l, network net); void resize_cost_layer(cost_layer *l, int inputs); #ifdef GPU -void forward_cost_layer_gpu(cost_layer l, network_state state); -void backward_cost_layer_gpu(const cost_layer l, network_state state); +void forward_cost_layer_gpu(cost_layer l, network net); +void backward_cost_layer_gpu(const cost_layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/crnn_layer.c b/image.darknet/inst/include/darknet/src/crnn_layer.c index 5495880..7dd29f6 100644 --- a/image.darknet/inst/include/darknet/src/crnn_layer.c +++ b/image.darknet/inst/include/darknet/src/crnn_layer.c @@ -48,17 +48,17 @@ layer make_crnn_layer(int batch, int h, int w, int c, int hidden_filters, int ou l.input_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_layer) = make_convolutional_layer(batch*steps, h, w, c, hidden_filters, 3, 1, 1, activation, batch_normalize, 0, 0, 0); + *(l.input_layer) = make_convolutional_layer(batch*steps, h, w, c, hidden_filters, 1, 3, 1, 1, activation, batch_normalize, 0, 0, 0); l.input_layer->batch = batch; l.self_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.self_layer) = make_convolutional_layer(batch*steps, h, w, hidden_filters, hidden_filters, 3, 1, 1, activation, batch_normalize, 0, 0, 0); + *(l.self_layer) = make_convolutional_layer(batch*steps, h, w, hidden_filters, hidden_filters, 1, 3, 1, 1, activation, batch_normalize, 0, 0, 0); l.self_layer->batch = batch; l.output_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.output_layer) = make_convolutional_layer(batch*steps, h, w, hidden_filters, output_filters, 3, 1, 1, activation, batch_normalize, 0, 0, 0); + *(l.output_layer) = make_convolutional_layer(batch*steps, h, w, hidden_filters, output_filters, 1, 3, 1, 1, activation, batch_normalize, 0, 0, 0); l.output_layer->batch = batch; l.output = l.output_layer->output; @@ -81,17 +81,17 @@ layer make_crnn_layer(int batch, int h, int w, int c, int hidden_filters, int ou return l; } -void update_crnn_layer(layer l, int batch, float learning_rate, float momentum, float decay) +void update_crnn_layer(layer l, update_args a) { - update_convolutional_layer(*(l.input_layer), batch, learning_rate, momentum, decay); - update_convolutional_layer(*(l.self_layer), batch, learning_rate, momentum, decay); - update_convolutional_layer(*(l.output_layer), batch, learning_rate, momentum, decay); + update_convolutional_layer(*(l.input_layer), a); + update_convolutional_layer(*(l.self_layer), a); + update_convolutional_layer(*(l.output_layer), a); } -void forward_crnn_layer(layer l, network_state state) +void forward_crnn_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -100,17 +100,17 @@ void forward_crnn_layer(layer l, network_state state) fill_cpu(l.outputs * l.batch * l.steps, 0, output_layer.delta, 1); fill_cpu(l.hidden * l.batch * l.steps, 0, self_layer.delta, 1); fill_cpu(l.hidden * l.batch * l.steps, 0, input_layer.delta, 1); - if(state.train) fill_cpu(l.hidden * l.batch, 0, l.state, 1); + if(net.train) fill_cpu(l.hidden * l.batch, 0, l.state, 1); for (i = 0; i < l.steps; ++i) { - s.input = state.input; + s.input = net.input; forward_convolutional_layer(input_layer, s); s.input = l.state; forward_convolutional_layer(self_layer, s); float *old_state = l.state; - if(state.train) l.state += l.hidden*l.batch; + if(net.train) l.state += l.hidden*l.batch; if(l.shortcut){ copy_cpu(l.hidden * l.batch, old_state, 1, l.state, 1); }else{ @@ -122,17 +122,16 @@ void forward_crnn_layer(layer l, network_state state) s.input = l.state; forward_convolutional_layer(output_layer, s); - state.input += l.inputs*l.batch; + net.input += l.inputs*l.batch; increment_layer(&input_layer, 1); increment_layer(&self_layer, 1); increment_layer(&output_layer, 1); } } -void backward_crnn_layer(layer l, network_state state) +void backward_crnn_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -168,8 +167,8 @@ void backward_crnn_layer(layer l, network_state state) copy_cpu(l.hidden*l.batch, self_layer.delta, 1, input_layer.delta, 1); if (i > 0 && l.shortcut) axpy_cpu(l.hidden*l.batch, 1, self_layer.delta, 1, self_layer.delta - l.hidden*l.batch, 1); - s.input = state.input + i*l.inputs*l.batch; - if(state.delta) s.delta = state.delta + i*l.inputs*l.batch; + s.input = net.input + i*l.inputs*l.batch; + if(net.delta) s.delta = net.delta + i*l.inputs*l.batch; else s.delta = 0; backward_convolutional_layer(input_layer, s); @@ -195,58 +194,57 @@ void push_crnn_layer(layer l) push_convolutional_layer(*(l.output_layer)); } -void update_crnn_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay) +void update_crnn_layer_gpu(layer l, update_args a) { - update_convolutional_layer_gpu(*(l.input_layer), batch, learning_rate, momentum, decay); - update_convolutional_layer_gpu(*(l.self_layer), batch, learning_rate, momentum, decay); - update_convolutional_layer_gpu(*(l.output_layer), batch, learning_rate, momentum, decay); + update_convolutional_layer_gpu(*(l.input_layer), a); + update_convolutional_layer_gpu(*(l.self_layer), a); + update_convolutional_layer_gpu(*(l.output_layer), a); } -void forward_crnn_layer_gpu(layer l, network_state state) +void forward_crnn_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); layer output_layer = *(l.output_layer); - fill_ongpu(l.outputs * l.batch * l.steps, 0, output_layer.delta_gpu, 1); - fill_ongpu(l.hidden * l.batch * l.steps, 0, self_layer.delta_gpu, 1); - fill_ongpu(l.hidden * l.batch * l.steps, 0, input_layer.delta_gpu, 1); - if(state.train) fill_ongpu(l.hidden * l.batch, 0, l.state_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, output_layer.delta_gpu, 1); + fill_gpu(l.hidden * l.batch * l.steps, 0, self_layer.delta_gpu, 1); + fill_gpu(l.hidden * l.batch * l.steps, 0, input_layer.delta_gpu, 1); + if(net.train) fill_gpu(l.hidden * l.batch, 0, l.state_gpu, 1); for (i = 0; i < l.steps; ++i) { - s.input = state.input; + s.input_gpu = net.input_gpu; forward_convolutional_layer_gpu(input_layer, s); - s.input = l.state_gpu; + s.input_gpu = l.state_gpu; forward_convolutional_layer_gpu(self_layer, s); float *old_state = l.state_gpu; - if(state.train) l.state_gpu += l.hidden*l.batch; + if(net.train) l.state_gpu += l.hidden*l.batch; if(l.shortcut){ - copy_ongpu(l.hidden * l.batch, old_state, 1, l.state_gpu, 1); + copy_gpu(l.hidden * l.batch, old_state, 1, l.state_gpu, 1); }else{ - fill_ongpu(l.hidden * l.batch, 0, l.state_gpu, 1); + fill_gpu(l.hidden * l.batch, 0, l.state_gpu, 1); } - axpy_ongpu(l.hidden * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); - axpy_ongpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.hidden * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); - s.input = l.state_gpu; + s.input_gpu = l.state_gpu; forward_convolutional_layer_gpu(output_layer, s); - state.input += l.inputs*l.batch; + net.input_gpu += l.inputs*l.batch; increment_layer(&input_layer, 1); increment_layer(&self_layer, 1); increment_layer(&output_layer, 1); } } -void backward_crnn_layer_gpu(layer l, network_state state) +void backward_crnn_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -256,25 +254,25 @@ void backward_crnn_layer_gpu(layer l, network_state state) increment_layer(&output_layer, l.steps - 1); l.state_gpu += l.hidden*l.batch*l.steps; for (i = l.steps-1; i >= 0; --i) { - copy_ongpu(l.hidden * l.batch, input_layer.output_gpu, 1, l.state_gpu, 1); - axpy_ongpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); + copy_gpu(l.hidden * l.batch, input_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); - s.input = l.state_gpu; - s.delta = self_layer.delta_gpu; + s.input_gpu = l.state_gpu; + s.delta_gpu = self_layer.delta_gpu; backward_convolutional_layer_gpu(output_layer, s); l.state_gpu -= l.hidden*l.batch; - s.input = l.state_gpu; - s.delta = self_layer.delta_gpu - l.hidden*l.batch; - if (i == 0) s.delta = 0; + s.input_gpu = l.state_gpu; + s.delta_gpu = self_layer.delta_gpu - l.hidden*l.batch; + if (i == 0) s.delta_gpu = 0; backward_convolutional_layer_gpu(self_layer, s); - copy_ongpu(l.hidden*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); - if (i > 0 && l.shortcut) axpy_ongpu(l.hidden*l.batch, 1, self_layer.delta_gpu, 1, self_layer.delta_gpu - l.hidden*l.batch, 1); - s.input = state.input + i*l.inputs*l.batch; - if(state.delta) s.delta = state.delta + i*l.inputs*l.batch; - else s.delta = 0; + copy_gpu(l.hidden*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); + if (i > 0 && l.shortcut) axpy_gpu(l.hidden*l.batch, 1, self_layer.delta_gpu, 1, self_layer.delta_gpu - l.hidden*l.batch, 1); + s.input_gpu = net.input_gpu + i*l.inputs*l.batch; + if(net.delta_gpu) s.delta_gpu = net.delta_gpu + i*l.inputs*l.batch; + else s.delta_gpu = 0; backward_convolutional_layer_gpu(input_layer, s); increment_layer(&input_layer, -1); diff --git a/image.darknet/inst/include/darknet/src/crnn_layer.h b/image.darknet/inst/include/darknet/src/crnn_layer.h index 0da942e..515f378 100644 --- a/image.darknet/inst/include/darknet/src/crnn_layer.h +++ b/image.darknet/inst/include/darknet/src/crnn_layer.h @@ -8,14 +8,14 @@ layer make_crnn_layer(int batch, int h, int w, int c, int hidden_filters, int output_filters, int steps, ACTIVATION activation, int batch_normalize); -void forward_crnn_layer(layer l, network_state state); -void backward_crnn_layer(layer l, network_state state); -void update_crnn_layer(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_crnn_layer(layer l, network net); +void backward_crnn_layer(layer l, network net); +void update_crnn_layer(layer l, update_args a); #ifdef GPU -void forward_crnn_layer_gpu(layer l, network_state state); -void backward_crnn_layer_gpu(layer l, network_state state); -void update_crnn_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_crnn_layer_gpu(layer l, network net); +void backward_crnn_layer_gpu(layer l, network net); +void update_crnn_layer_gpu(layer l, update_args a); void push_crnn_layer(layer l); void pull_crnn_layer(layer l); #endif diff --git a/image.darknet/inst/include/darknet/src/crop_layer.c b/image.darknet/inst/include/darknet/src/crop_layer.c index 11c59b4..3b91852 100644 --- a/image.darknet/inst/include/darknet/src/crop_layer.c +++ b/image.darknet/inst/include/darknet/src/crop_layer.c @@ -10,8 +10,8 @@ image get_crop_image(crop_layer l) return float_to_image(w,h,c,l.output); } -void backward_crop_layer(const crop_layer l, network_state state){} -void backward_crop_layer_gpu(const crop_layer l, network_state state){} +void backward_crop_layer(const crop_layer l, network net){} +void backward_crop_layer_gpu(const crop_layer l, network net){} crop_layer make_crop_layer(int batch, int h, int w, int c, int crop_height, int crop_width, int flip, float angle, float saturation, float exposure) { @@ -64,7 +64,7 @@ void resize_crop_layer(layer *l, int w, int h) } -void forward_crop_layer(const crop_layer l, network_state state) +void forward_crop_layer(const crop_layer l, network net) { int i,j,c,b,row,col; int index; @@ -78,7 +78,7 @@ void forward_crop_layer(const crop_layer l, network_state state) scale = 1; trans = 0; } - if(!state.train){ + if(!net.train){ flip = 0; dh = (l.h - l.out_h)/2; dw = (l.w - l.out_w)/2; @@ -94,7 +94,7 @@ void forward_crop_layer(const crop_layer l, network_state state) } row = i + dh; index = col+l.w*(row+l.h*(c + l.c*b)); - l.output[count++] = state.input[index]*scale + trans; + l.output[count++] = net.input[index]*scale + trans; } } } diff --git a/image.darknet/inst/include/darknet/src/crop_layer.h b/image.darknet/inst/include/darknet/src/crop_layer.h index 3aa2d3d..3b5883c 100644 --- a/image.darknet/inst/include/darknet/src/crop_layer.h +++ b/image.darknet/inst/include/darknet/src/crop_layer.h @@ -9,11 +9,11 @@ typedef layer crop_layer; image get_crop_image(crop_layer l); crop_layer make_crop_layer(int batch, int h, int w, int c, int crop_height, int crop_width, int flip, float angle, float saturation, float exposure); -void forward_crop_layer(const crop_layer l, network_state state); +void forward_crop_layer(const crop_layer l, network net); void resize_crop_layer(layer *l, int w, int h); #ifdef GPU -void forward_crop_layer_gpu(crop_layer l, network_state state); +void forward_crop_layer_gpu(crop_layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/crop_layer_kernels.cu b/image.darknet/inst/include/darknet/src/crop_layer_kernels.cu index 8a08630..b5b9f55 100644 --- a/image.darknet/inst/include/darknet/src/crop_layer_kernels.cu +++ b/image.darknet/inst/include/darknet/src/crop_layer_kernels.cu @@ -113,9 +113,9 @@ __global__ void levels_image_kernel(float *image, float *rand, int batch, int w, float r3 = rand[8*id + 3]; saturation = r0*(saturation - 1) + 1; - saturation = (r1 > .5) ? 1./saturation : saturation; + saturation = (r1 > .5f) ? 1.f/saturation : saturation; exposure = r2*(exposure - 1) + 1; - exposure = (r3 > .5) ? 1./exposure : exposure; + exposure = (r3 > .5f) ? 1.f/exposure : exposure; size_t offset = id * h * w * 3; image += offset; @@ -131,9 +131,9 @@ __global__ void levels_image_kernel(float *image, float *rand, int batch, int w, } else { shift = 0; } - image[x + w*(y + h*0)] = rgb.x*scale + translate + (rshift - .5)*shift; - image[x + w*(y + h*1)] = rgb.y*scale + translate + (gshift - .5)*shift; - image[x + w*(y + h*2)] = rgb.z*scale + translate + (bshift - .5)*shift; + image[x + w*(y + h*0)] = rgb.x*scale + translate + (rshift - .5f)*shift; + image[x + w*(y + h*1)] = rgb.y*scale + translate + (gshift - .5f)*shift; + image[x + w*(y + h*2)] = rgb.z*scale + translate + (bshift - .5f)*shift; } __global__ void forward_crop_layer_kernel(float *input, float *rand, int size, int c, int h, int w, int crop_height, int crop_width, int train, int flip, float angle, float *output) @@ -141,8 +141,8 @@ __global__ void forward_crop_layer_kernel(float *input, float *rand, int size, i int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; if(id >= size) return; - float cx = w/2.; - float cy = h/2.; + float cx = w/2.f; + float cy = h/2.f; int count = id; int j = id % crop_width; @@ -160,11 +160,11 @@ __global__ void forward_crop_layer_kernel(float *input, float *rand, int size, i float dw = (w - crop_width)*r4; float dh = (h - crop_height)*r5; - flip = (flip && (r6 > .5)); + flip = (flip && (r6 > .5f)); angle = 2*angle*r7 - angle; if(!train){ - dw = (w - crop_width)/2.; - dh = (h - crop_height)/2.; + dw = (w - crop_width)/2.f; + dh = (h - crop_height)/2.f; flip = 0; angle = 0; } @@ -174,17 +174,17 @@ __global__ void forward_crop_layer_kernel(float *input, float *rand, int size, i float x = (flip) ? w - dw - j - 1 : j + dw; float y = i + dh; - float rx = cos(angle)*(x-cx) - sin(angle)*(y-cy) + cx; - float ry = sin(angle)*(x-cx) + cos(angle)*(y-cy) + cy; + float rx = cosf(angle)*(x-cx) - sinf(angle)*(y-cy) + cx; + float ry = sinf(angle)*(x-cx) + cosf(angle)*(y-cy) + cy; output[count] = bilinear_interpolate_kernel(input, w, h, rx, ry, k); } -extern "C" void forward_crop_layer_gpu(crop_layer layer, network_state state) +extern "C" void forward_crop_layer_gpu(crop_layer layer, network net) { cuda_random(layer.rand_gpu, layer.batch*8); - float radians = layer.angle*3.14159265/180.; + float radians = layer.angle*3.14159265f/180.f; float scale = 2; float translate = -1; @@ -195,12 +195,12 @@ extern "C" void forward_crop_layer_gpu(crop_layer layer, network_state state) int size = layer.batch * layer.w * layer.h; - levels_image_kernel<<>>(state.input, layer.rand_gpu, layer.batch, layer.w, layer.h, state.train, layer.saturation, layer.exposure, translate, scale, layer.shift); + levels_image_kernel<<>>(net.input_gpu, layer.rand_gpu, layer.batch, layer.w, layer.h, net.train, layer.saturation, layer.exposure, translate, scale, layer.shift); check_error(cudaPeekAtLastError()); size = layer.batch*layer.c*layer.out_w*layer.out_h; - forward_crop_layer_kernel<<>>(state.input, layer.rand_gpu, size, layer.c, layer.h, layer.w, layer.out_h, layer.out_w, state.train, layer.flip, radians, layer.output_gpu); + forward_crop_layer_kernel<<>>(net.input_gpu, layer.rand_gpu, size, layer.c, layer.h, layer.w, layer.out_h, layer.out_w, net.train, layer.flip, radians, layer.output_gpu); check_error(cudaPeekAtLastError()); /* diff --git a/image.darknet/inst/include/darknet/src/cuda.c b/image.darknet/inst/include/darknet/src/cuda.c index 1b51271..48aba6e 100644 --- a/image.darknet/inst/include/darknet/src/cuda.c +++ b/image.darknet/inst/include/darknet/src/cuda.c @@ -5,7 +5,7 @@ int gpu_index = 0; #include "cuda.h" #include "utils.h" #include "blas.h" -#include "assert.h" +#include #include #include @@ -96,6 +96,8 @@ float *cuda_make_array(float *x, size_t n) if(x){ status = cudaMemcpy(x_gpu, x, size, cudaMemcpyHostToDevice); check_error(status); + } else { + fill_gpu(n, 0, x_gpu, 1); } if(!x_gpu) error("Cuda malloc failed\n"); return x_gpu; @@ -128,12 +130,17 @@ float cuda_compare(float *x_gpu, float *x, size_t n, char *s) return err; } -int *cuda_make_int_array(size_t n) +int *cuda_make_int_array(int *x, size_t n) { int *x_gpu; size_t size = sizeof(int)*n; cudaError_t status = cudaMalloc((void **)&x_gpu, size); check_error(status); + if(x){ + status = cudaMemcpy(x_gpu, x, size, cudaMemcpyHostToDevice); + check_error(status); + } + if(!x_gpu) error("Cuda malloc failed\n"); return x_gpu; } @@ -157,4 +164,15 @@ void cuda_pull_array(float *x_gpu, float *x, size_t n) check_error(status); } +float cuda_mag_array(float *x_gpu, size_t n) +{ + float *temp = calloc(n, sizeof(float)); + cuda_pull_array(x_gpu, temp, n); + float m = mag_array(temp, n); + free(temp); + return m; +} +#else +void cuda_set_device(int n){} + #endif diff --git a/image.darknet/inst/include/darknet/src/cuda.h b/image.darknet/inst/include/darknet/src/cuda.h index 29b1eef..a1bc216 100644 --- a/image.darknet/inst/include/darknet/src/cuda.h +++ b/image.darknet/inst/include/darknet/src/cuda.h @@ -1,28 +1,13 @@ #ifndef CUDA_H #define CUDA_H -extern int gpu_index; +#include "darknet.h" #ifdef GPU -#define BLOCK 512 - -#include "cuda_runtime.h" -#include "curand.h" -#include "cublas_v2.h" - -#ifdef CUDNN -#include "cudnn.h" -#endif - void check_error(cudaError_t status); cublasHandle_t blas_handle(); -float *cuda_make_array(float *x, size_t n); -int *cuda_make_int_array(size_t n); -void cuda_push_array(float *x_gpu, float *x, size_t n); -void cuda_pull_array(float *x_gpu, float *x, size_t n); -void cuda_set_device(int n); -void cuda_free(float *x_gpu); +int *cuda_make_int_array(int *x, size_t n); void cuda_random(float *x_gpu, size_t n); float cuda_compare(float *x_gpu, float *x, size_t n, char *s); dim3 cuda_gridsize(size_t n); diff --git a/image.darknet/inst/include/darknet/src/data.c b/image.darknet/inst/include/darknet/src/data.c index 05e5a91..59051b4 100644 --- a/image.darknet/inst/include/darknet/src/data.c +++ b/image.darknet/inst/include/darknet/src/data.c @@ -102,7 +102,7 @@ matrix load_image_paths(char **paths, int n, int w, int h) return X; } -matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure) +matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure, int center) { int i; matrix X; @@ -112,7 +112,12 @@ matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, for(i = 0; i < n; ++i){ image im = load_image_color(paths[i], 0, 0); - image crop = random_augment_image(im, angle, aspect, min, max, size); + image crop; + if(center){ + crop = center_crop_image(im, size, size); + } else { + crop = random_augment_image(im, angle, aspect, min, max, size, size); + } int flip = rand()%2; if (flip) flip_image(crop); random_distort_image(crop, hue, saturation, exposure); @@ -122,6 +127,7 @@ matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, show_image(crop, "crop"); cvWaitKey(0); */ + //grayscale_image_3c(crop); free_image(im); X.vals[i] = crop.data; X.cols = crop.h*crop.w*crop.c; @@ -132,14 +138,18 @@ matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, box_label *read_boxes(char *filename, int *n) { - box_label *boxes = calloc(1, sizeof(box_label)); FILE *file = fopen(filename, "r"); if(!file) file_error(filename); float x, y, h, w; int id; int count = 0; + int size = 64; + box_label *boxes = calloc(size, sizeof(box_label)); while(fscanf(file, "%d %f %f %f %f", &id, &x, &y, &w, &h) == 5){ - boxes = realloc(boxes, (count+1)*sizeof(box_label)); + if(count == size) { + size = size * 2; + boxes = realloc(boxes, size*sizeof(box_label)); + } boxes[count].id = id; boxes[count].x = x; boxes[count].y = y; @@ -221,7 +231,7 @@ void fill_truth_swag(char *path, float *truth, int classes, int flip, float dx, int id; int i; - for (i = 0; i < count && i < 30; ++i) { + for (i = 0; i < count && i < 90; ++i) { x = boxes[i].x; y = boxes[i].y; w = boxes[i].w; @@ -290,6 +300,150 @@ void fill_truth_region(char *path, float *truth, int classes, int num_boxes, int free(boxes); } +void load_rle(image im, int *rle, int n) +{ + int count = 0; + int curr = 0; + int i,j; + for(i = 0; i < n; ++i){ + for(j = 0; j < rle[i]; ++j){ + im.data[count++] = curr; + } + curr = 1 - curr; + } + for(; count < im.h*im.w*im.c; ++count){ + im.data[count] = curr; + } +} + +void or_image(image src, image dest, int c) +{ + int i; + for(i = 0; i < src.w*src.h; ++i){ + if(src.data[i]) dest.data[dest.w*dest.h*c + i] = 1; + } +} + +void exclusive_image(image src) +{ + int k, j, i; + int s = src.w*src.h; + for(k = 0; k < src.c-1; ++k){ + for(i = 0; i < s; ++i){ + if (src.data[k*s + i]){ + for(j = k+1; j < src.c; ++j){ + src.data[j*s + i] = 0; + } + } + } + } +} + +box bound_image(image im) +{ + int x,y; + int minx = im.w; + int miny = im.h; + int maxx = 0; + int maxy = 0; + for(y = 0; y < im.h; ++y){ + for(x = 0; x < im.w; ++x){ + if(im.data[y*im.w + x]){ + minx = (x < minx) ? x : minx; + miny = (y < miny) ? y : miny; + maxx = (x > maxx) ? x : maxx; + maxy = (y > maxy) ? y : maxy; + } + } + } + box b = {minx, miny, maxx-minx + 1, maxy-miny + 1}; + //printf("%f %f %f %f\n", b.x, b.y, b.w, b.h); + return b; +} + +void fill_truth_iseg(char *path, int num_boxes, float *truth, int classes, int w, int h, augment_args aug, int flip, int mw, int mh) +{ + char labelpath[4096]; + find_replace(path, "images", "mask", labelpath); + find_replace(labelpath, "JPEGImages", "mask", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + FILE *file = fopen(labelpath, "r"); + if(!file) file_error(labelpath); + char buff[32788]; + int id; + int i = 0; + int j; + image part = make_image(w, h, 1); + while((fscanf(file, "%d %s", &id, buff) == 2) && i < num_boxes){ + int n = 0; + int *rle = read_intlist(buff, &n, 0); + load_rle(part, rle, n); + image sized = rotate_crop_image(part, aug.rad, aug.scale, aug.w, aug.h, aug.dx, aug.dy, aug.aspect); + if(flip) flip_image(sized); + + image mask = resize_image(sized, mw, mh); + truth[i*(mw*mh+1)] = id; + for(j = 0; j < mw*mh; ++j){ + truth[i*(mw*mh + 1) + 1 + j] = mask.data[j]; + } + ++i; + + free_image(mask); + free_image(sized); + free(rle); + } + if(i < num_boxes) truth[i*(mw*mh+1)] = -1; + fclose(file); + free_image(part); +} + +void fill_truth_mask(char *path, int num_boxes, float *truth, int classes, int w, int h, augment_args aug, int flip, int mw, int mh) +{ + char labelpath[4096]; + find_replace(path, "images", "mask", labelpath); + find_replace(labelpath, "JPEGImages", "mask", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + FILE *file = fopen(labelpath, "r"); + if(!file) file_error(labelpath); + char buff[32788]; + int id; + int i = 0; + image part = make_image(w, h, 1); + while((fscanf(file, "%d %s", &id, buff) == 2) && i < num_boxes){ + int n = 0; + int *rle = read_intlist(buff, &n, 0); + load_rle(part, rle, n); + image sized = rotate_crop_image(part, aug.rad, aug.scale, aug.w, aug.h, aug.dx, aug.dy, aug.aspect); + if(flip) flip_image(sized); + box b = bound_image(sized); + if(b.w > 0){ + image crop = crop_image(sized, b.x, b.y, b.w, b.h); + image mask = resize_image(crop, mw, mh); + truth[i*(4 + mw*mh + 1) + 0] = (b.x + b.w/2.)/sized.w; + truth[i*(4 + mw*mh + 1) + 1] = (b.y + b.h/2.)/sized.h; + truth[i*(4 + mw*mh + 1) + 2] = b.w/sized.w; + truth[i*(4 + mw*mh + 1) + 3] = b.h/sized.h; + int j; + for(j = 0; j < mw*mh; ++j){ + truth[i*(4 + mw*mh + 1) + 4 + j] = mask.data[j]; + } + truth[i*(4 + mw*mh + 1) + 4 + mw*mh] = id; + free_image(crop); + free_image(mask); + ++i; + } + free_image(sized); + free(rle); + } + fclose(file); + free_image(part); +} + + void fill_truth_detection(char *path, int num_boxes, float *truth, int classes, int flip, float dx, float dy, float sx, float sy) { char labelpath[4096]; @@ -309,6 +463,7 @@ void fill_truth_detection(char *path, int num_boxes, float *truth, int classes, float x,y,w,h; int id; int i; + int sub = 0; for (i = 0; i < count; ++i) { x = boxes[i].x; @@ -317,13 +472,16 @@ void fill_truth_detection(char *path, int num_boxes, float *truth, int classes, h = boxes[i].h; id = boxes[i].id; - if ((w < .005 || h < .005)) continue; + if ((w < .001 || h < .001)) { + ++sub; + continue; + } - truth[i*5+0] = x; - truth[i*5+1] = y; - truth[i*5+2] = w; - truth[i*5+3] = h; - truth[i*5+4] = id; + truth[(i-sub)*5+0] = x; + truth[(i-sub)*5+1] = y; + truth[(i-sub)*5+2] = w; + truth[(i-sub)*5+3] = h; + truth[(i-sub)*5+4] = id; } free(boxes); } @@ -391,9 +549,10 @@ void fill_truth(char *path, char **labels, int k, float *truth) if(strstr(path, labels[i])){ truth[i] = 1; ++count; + //printf("%s %s %d\n", path, labels[i], i); } } - if(count != 1) printf("Too many or too few labels: %d, %s\n", count, path); + if(count != 1 && (k != 1 || count != 0)) printf("Too many or too few labels: %d, %s\n", count, path); } void fill_hierarchy(float *truth, int k, tree *hierarchy) @@ -428,6 +587,36 @@ void fill_hierarchy(float *truth, int k, tree *hierarchy) } } +matrix load_regression_labels_paths(char **paths, int n, int k) +{ + matrix y = make_matrix(n, k); + int i,j; + for(i = 0; i < n; ++i){ + char labelpath[4096]; + find_replace(paths[i], "images", "labels", labelpath); + find_replace(labelpath, "JPEGImages", "labels", labelpath); + find_replace(labelpath, ".BMP", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPeG", ".txt", labelpath); + find_replace(labelpath, ".Jpeg", ".txt", labelpath); + find_replace(labelpath, ".PNG", ".txt", labelpath); + find_replace(labelpath, ".TIF", ".txt", labelpath); + find_replace(labelpath, ".bmp", ".txt", labelpath); + find_replace(labelpath, ".jpeg", ".txt", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".png", ".txt", labelpath); + find_replace(labelpath, ".tif", ".txt", labelpath); + + FILE *file = fopen(labelpath, "r"); + for(j = 0; j < k; ++j){ + fscanf(file, "%f", &(y.vals[i][j])); + } + fclose(file); + } + return y; +} + matrix load_labels_paths(char **paths, int n, char **labels, int k, tree *hierarchy) { matrix y = make_matrix(n, k); @@ -445,18 +634,14 @@ matrix load_tags_paths(char **paths, int n, int k) { matrix y = make_matrix(n, k); int i; - int count = 0; + //int count = 0; for(i = 0; i < n; ++i){ char label[4096]; - find_replace(paths[i], "imgs", "labels", label); - find_replace(label, "_iconl.jpeg", ".txt", label); + find_replace(paths[i], "images", "labels", label); + find_replace(label, ".jpg", ".txt", label); FILE *file = fopen(label, "r"); - if(!file){ - find_replace(label, "labels", "labels2", label); - file = fopen(label, "r"); - if(!file) continue; - } - ++count; + if (!file) continue; + //++count; int tag; while(fscanf(file, "%d", &tag) == 1){ if(tag < k){ @@ -465,7 +650,7 @@ matrix load_tags_paths(char **paths, int n, int k) } fclose(file); } - printf("%d/%d\n", count, n); + //printf("%d/%d\n", count, n); return y; } @@ -488,6 +673,195 @@ void free_data(data d) } } +image get_segmentation_image(char *path, int w, int h, int classes) +{ + char labelpath[4096]; + find_replace(path, "images", "mask", labelpath); + find_replace(labelpath, "JPEGImages", "mask", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + image mask = make_image(w, h, classes); + FILE *file = fopen(labelpath, "r"); + if(!file) file_error(labelpath); + char buff[32788]; + int id; + image part = make_image(w, h, 1); + while(fscanf(file, "%d %s", &id, buff) == 2){ + int n = 0; + int *rle = read_intlist(buff, &n, 0); + load_rle(part, rle, n); + or_image(part, mask, id); + free(rle); + } + //exclusive_image(mask); + fclose(file); + free_image(part); + return mask; +} + +image get_segmentation_image2(char *path, int w, int h, int classes) +{ + char labelpath[4096]; + find_replace(path, "images", "mask", labelpath); + find_replace(labelpath, "JPEGImages", "mask", labelpath); + find_replace(labelpath, ".jpg", ".txt", labelpath); + find_replace(labelpath, ".JPG", ".txt", labelpath); + find_replace(labelpath, ".JPEG", ".txt", labelpath); + image mask = make_image(w, h, classes+1); + int i; + for(i = 0; i < w*h; ++i){ + mask.data[w*h*classes + i] = 1; + } + FILE *file = fopen(labelpath, "r"); + if(!file) file_error(labelpath); + char buff[32788]; + int id; + image part = make_image(w, h, 1); + while(fscanf(file, "%d %s", &id, buff) == 2){ + int n = 0; + int *rle = read_intlist(buff, &n, 0); + load_rle(part, rle, n); + or_image(part, mask, id); + for(i = 0; i < w*h; ++i){ + if(part.data[i]) mask.data[w*h*classes + i] = 0; + } + free(rle); + } + //exclusive_image(mask); + fclose(file); + free_image(part); + return mask; +} + +data load_data_seg(int n, char **paths, int m, int w, int h, int classes, int min, int max, float angle, float aspect, float hue, float saturation, float exposure, int div) +{ + char **random_paths = get_random_paths(paths, n, m); + int i; + data d = {0}; + d.shallow = 0; + + d.X.rows = n; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + d.X.cols = h*w*3; + + + d.y.rows = n; + d.y.cols = h*w*classes/div/div; + d.y.vals = calloc(d.X.rows, sizeof(float*)); + + for(i = 0; i < n; ++i){ + image orig = load_image_color(random_paths[i], 0, 0); + augment_args a = random_augment_args(orig, angle, aspect, min, max, w, h); + image sized = rotate_crop_image(orig, a.rad, a.scale, a.w, a.h, a.dx, a.dy, a.aspect); + + int flip = rand()%2; + if(flip) flip_image(sized); + random_distort_image(sized, hue, saturation, exposure); + d.X.vals[i] = sized.data; + + image mask = get_segmentation_image(random_paths[i], orig.w, orig.h, classes); + //image mask = make_image(orig.w, orig.h, classes+1); + image sized_m = rotate_crop_image(mask, a.rad, a.scale/div, a.w/div, a.h/div, a.dx/div, a.dy/div, a.aspect); + + if(flip) flip_image(sized_m); + d.y.vals[i] = sized_m.data; + + free_image(orig); + free_image(mask); + + /* + image rgb = mask_to_rgb(sized_m, classes); + show_image(rgb, "part"); + show_image(sized, "orig"); + cvWaitKey(0); + free_image(rgb); + */ + } + free(random_paths); + return d; +} + +data load_data_iseg(int n, char **paths, int m, int w, int h, int classes, int boxes, int div, int min, int max, float angle, float aspect, float hue, float saturation, float exposure) +{ + char **random_paths = get_random_paths(paths, n, m); + int i; + data d = {0}; + d.shallow = 0; + + d.X.rows = n; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + d.X.cols = h*w*3; + + d.y = make_matrix(n, (((w/div)*(h/div))+1)*boxes); + + for(i = 0; i < n; ++i){ + image orig = load_image_color(random_paths[i], 0, 0); + augment_args a = random_augment_args(orig, angle, aspect, min, max, w, h); + image sized = rotate_crop_image(orig, a.rad, a.scale, a.w, a.h, a.dx, a.dy, a.aspect); + + int flip = rand()%2; + if(flip) flip_image(sized); + random_distort_image(sized, hue, saturation, exposure); + d.X.vals[i] = sized.data; + //show_image(sized, "image"); + + fill_truth_iseg(random_paths[i], boxes, d.y.vals[i], classes, orig.w, orig.h, a, flip, w/div, h/div); + + free_image(orig); + + /* + image rgb = mask_to_rgb(sized_m, classes); + show_image(rgb, "part"); + show_image(sized, "orig"); + cvWaitKey(0); + free_image(rgb); + */ + } + free(random_paths); + return d; +} + +data load_data_mask(int n, char **paths, int m, int w, int h, int classes, int boxes, int coords, int min, int max, float angle, float aspect, float hue, float saturation, float exposure) +{ + char **random_paths = get_random_paths(paths, n, m); + int i; + data d = {0}; + d.shallow = 0; + + d.X.rows = n; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + d.X.cols = h*w*3; + + d.y = make_matrix(n, (coords+1)*boxes); + + for(i = 0; i < n; ++i){ + image orig = load_image_color(random_paths[i], 0, 0); + augment_args a = random_augment_args(orig, angle, aspect, min, max, w, h); + image sized = rotate_crop_image(orig, a.rad, a.scale, a.w, a.h, a.dx, a.dy, a.aspect); + + int flip = rand()%2; + if(flip) flip_image(sized); + random_distort_image(sized, hue, saturation, exposure); + d.X.vals[i] = sized.data; + //show_image(sized, "image"); + + fill_truth_mask(random_paths[i], boxes, d.y.vals[i], classes, orig.w, orig.h, a, flip, 14, 14); + + free_image(orig); + + /* + image rgb = mask_to_rgb(sized_m, classes); + show_image(rgb, "part"); + show_image(sized, "orig"); + cvWaitKey(0); + free_image(rgb); + */ + } + free(random_paths); + return d; +} + data load_data_region(int n, char **paths, int m, int w, int h, int size, int classes, float jitter, float hue, float saturation, float exposure) { char **random_paths = get_random_paths(paths, n, m); @@ -624,7 +998,7 @@ data load_data_swag(char **paths, int n, int classes, float jitter) d.X.vals = calloc(d.X.rows, sizeof(float*)); d.X.cols = h*w*3; - int k = (4+classes)*30; + int k = (4+classes)*90; d.y = make_matrix(1, k); int dw = w*jitter; @@ -673,45 +1047,46 @@ data load_data_detection(int n, char **paths, int m, int w, int h, int boxes, in d.y = make_matrix(n, 5*boxes); for(i = 0; i < n; ++i){ image orig = load_image_color(random_paths[i], 0, 0); + image sized = make_image(w, h, orig.c); + fill_image(sized, .5); - int oh = orig.h; - int ow = orig.w; + float dw = jitter * orig.w; + float dh = jitter * orig.h; - int dw = (ow*jitter); - int dh = (oh*jitter); + float new_ar = (orig.w + rand_uniform(-dw, dw)) / (orig.h + rand_uniform(-dh, dh)); + //float scale = rand_uniform(.25, 2); + float scale = 1; - int pleft = rand_uniform(-dw, dw); - int pright = rand_uniform(-dw, dw); - int ptop = rand_uniform(-dh, dh); - int pbot = rand_uniform(-dh, dh); + float nw, nh; - int swidth = ow - pleft - pright; - int sheight = oh - ptop - pbot; + if(new_ar < 1){ + nh = scale * h; + nw = nh * new_ar; + } else { + nw = scale * w; + nh = nw / new_ar; + } - float sx = (float)swidth / ow; - float sy = (float)sheight / oh; + float dx = rand_uniform(0, w - nw); + float dy = rand_uniform(0, h - nh); - int flip = rand()%2; - image cropped = crop_image(orig, pleft, ptop, swidth, sheight); + place_image(orig, nw, nh, dx, dy, sized); - float dx = ((float)pleft/ow)/sx; - float dy = ((float)ptop /oh)/sy; + random_distort_image(sized, hue, saturation, exposure); - image sized = resize_image(cropped, w, h); + int flip = rand()%2; if(flip) flip_image(sized); - random_distort_image(sized, hue, saturation, exposure); d.X.vals[i] = sized.data; - fill_truth_detection(random_paths[i], boxes, d.y.vals[i], classes, flip, dx, dy, 1./sx, 1./sy); + + fill_truth_detection(random_paths[i], boxes, d.y.vals[i], classes, flip, -dx/w, -dy/h, nw/w, nh/h); free_image(orig); - free_image(cropped); } free(random_paths); return d; } - void *load_thread(void *ptr) { //printf("Loading data: %d\n", rand()); @@ -722,12 +1097,20 @@ void *load_thread(void *ptr) if (a.type == OLD_CLASSIFICATION_DATA){ *a.d = load_data_old(a.paths, a.n, a.m, a.labels, a.classes, a.w, a.h); + } else if (a.type == REGRESSION_DATA){ + *a.d = load_data_regression(a.paths, a.n, a.m, a.classes, a.min, a.max, a.size, a.angle, a.aspect, a.hue, a.saturation, a.exposure); } else if (a.type == CLASSIFICATION_DATA){ - *a.d = load_data_augment(a.paths, a.n, a.m, a.labels, a.classes, a.hierarchy, a.min, a.max, a.size, a.angle, a.aspect, a.hue, a.saturation, a.exposure); + *a.d = load_data_augment(a.paths, a.n, a.m, a.labels, a.classes, a.hierarchy, a.min, a.max, a.size, a.angle, a.aspect, a.hue, a.saturation, a.exposure, a.center); } else if (a.type == SUPER_DATA){ *a.d = load_data_super(a.paths, a.n, a.m, a.w, a.h, a.scale); } else if (a.type == WRITING_DATA){ *a.d = load_data_writing(a.paths, a.n, a.m, a.w, a.h, a.out_w, a.out_h); + } else if (a.type == ISEG_DATA){ + *a.d = load_data_iseg(a.n, a.paths, a.m, a.w, a.h, a.classes, a.num_boxes, a.scale, a.min, a.max, a.angle, a.aspect, a.hue, a.saturation, a.exposure); + } else if (a.type == INSTANCE_DATA){ + *a.d = load_data_mask(a.n, a.paths, a.m, a.w, a.h, a.classes, a.num_boxes, a.coords, a.min, a.max, a.angle, a.aspect, a.hue, a.saturation, a.exposure); + } else if (a.type == SEGMENTATION_DATA){ + *a.d = load_data_seg(a.n, a.paths, a.m, a.w, a.h, a.classes, a.min, a.max, a.angle, a.aspect, a.hue, a.saturation, a.exposure, a.scale); } else if (a.type == REGION_DATA){ *a.d = load_data_region(a.n, a.paths, a.m, a.w, a.h, a.num_boxes, a.classes, a.jitter, a.hue, a.saturation, a.exposure); } else if (a.type == DETECTION_DATA){ @@ -739,6 +1122,9 @@ void *load_thread(void *ptr) } else if (a.type == IMAGE_DATA){ *(a.im) = load_image_color(a.path, 0, 0); *(a.resized) = resize_image(*(a.im), a.w, a.h); + } else if (a.type == LETTERBOX_DATA){ + *(a.im) = load_image_color(a.path, 0, 0); + *(a.resized) = letterbox_image(*(a.im), a.w, a.h); } else if (a.type == TAG_DATA){ *a.d = load_data_tag(a.paths, a.n, a.m, a.classes, a.min, a.max, a.size, a.angle, a.aspect, a.hue, a.saturation, a.exposure); } @@ -784,6 +1170,13 @@ void *load_threads(void *ptr) return 0; } +void load_data_blocking(load_args args) +{ + struct load_args *ptr = calloc(1, sizeof(struct load_args)); + *ptr = args; + load_thread(ptr); +} + pthread_t load_data(load_args args) { pthread_t thread; @@ -863,12 +1256,95 @@ data load_data_super(char **paths, int n, int m, int w, int h, int scale) return d; } -data load_data_augment(char **paths, int n, int m, char **labels, int k, tree *hierarchy, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure) +data load_data_regression(char **paths, int n, int m, int k, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure) { if(m) paths = get_random_paths(paths, n, m); data d = {0}; d.shallow = 0; - d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure); + d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure, 0); + d.y = load_regression_labels_paths(paths, n, k); + if(m) free(paths); + return d; +} + +data select_data(data *orig, int *inds) +{ + data d = {0}; + d.shallow = 1; + d.w = orig[0].w; + d.h = orig[0].h; + + d.X.rows = orig[0].X.rows; + d.y.rows = orig[0].X.rows; + + d.X.cols = orig[0].X.cols; + d.y.cols = orig[0].y.cols; + + d.X.vals = calloc(orig[0].X.rows, sizeof(float *)); + d.y.vals = calloc(orig[0].y.rows, sizeof(float *)); + int i; + for(i = 0; i < d.X.rows; ++i){ + d.X.vals[i] = orig[inds[i]].X.vals[i]; + d.y.vals[i] = orig[inds[i]].y.vals[i]; + } + return d; +} + +data *tile_data(data orig, int divs, int size) +{ + data *ds = calloc(divs*divs, sizeof(data)); + int i, j; +#pragma omp parallel for + for(i = 0; i < divs*divs; ++i){ + data d; + d.shallow = 0; + d.w = orig.w/divs * size; + d.h = orig.h/divs * size; + d.X.rows = orig.X.rows; + d.X.cols = d.w*d.h*3; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + + d.y = copy_matrix(orig.y); +#pragma omp parallel for + for(j = 0; j < orig.X.rows; ++j){ + int x = (i%divs) * orig.w / divs - (d.w - orig.w/divs)/2; + int y = (i/divs) * orig.h / divs - (d.h - orig.h/divs)/2; + image im = float_to_image(orig.w, orig.h, 3, orig.X.vals[j]); + d.X.vals[j] = crop_image(im, x, y, d.w, d.h).data; + } + ds[i] = d; + } + return ds; +} + +data resize_data(data orig, int w, int h) +{ + data d = {0}; + d.shallow = 0; + d.w = w; + d.h = h; + int i; + d.X.rows = orig.X.rows; + d.X.cols = w*h*3; + d.X.vals = calloc(d.X.rows, sizeof(float*)); + + d.y = copy_matrix(orig.y); +#pragma omp parallel for + for(i = 0; i < orig.X.rows; ++i){ + image im = float_to_image(orig.w, orig.h, 3, orig.X.vals[i]); + d.X.vals[i] = resize_image(im, w, h).data; + } + return d; +} + +data load_data_augment(char **paths, int n, int m, char **labels, int k, tree *hierarchy, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure, int center) +{ + if(m) paths = get_random_paths(paths, n, m); + data d = {0}; + d.shallow = 0; + d.w=size; + d.h=size; + d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure, center); d.y = load_labels_paths(paths, n, labels, k, hierarchy); if(m) free(paths); return d; @@ -881,7 +1357,7 @@ data load_data_tag(char **paths, int n, int m, int k, int min, int max, int size d.w = size; d.h = size; d.shallow = 0; - d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure); + d.X = load_image_augment_paths(paths, n, min, max, size, angle, aspect, hue, saturation, exposure, 0); d.y = load_tags_paths(paths, n, k); if(m) free(paths); return d; @@ -909,6 +1385,8 @@ data concat_data(data d1, data d2) d.shallow = 1; d.X = concat_matrix(d1.X, d2.X); d.y = concat_matrix(d1.y, d2.y); + d.w = d1.w; + d.h = d1.h; return d; } @@ -962,7 +1440,6 @@ data load_cifar10_data(char *filename) X.vals[i][j] = (double)bytes[j+1]; } } - //translate_data_rows(d, -128); scale_data_rows(d, 1./255); //normalize_data_rows(d); fclose(fp); @@ -985,7 +1462,7 @@ void get_next_batch(data d, int n, int offset, float *X, float *y) for(j = 0; j < n; ++j){ int index = offset + j; memcpy(X+j*d.X.cols, d.X.vals[index], d.X.cols*sizeof(float)); - memcpy(y+j*d.y.cols, d.y.vals[index], d.y.cols*sizeof(float)); + if(y) memcpy(y+j*d.y.cols, d.y.vals[index], d.y.cols*sizeof(float)); } } @@ -1029,7 +1506,6 @@ data load_all_cifar10() fclose(fp); } //normalize_data_rows(d); - //translate_data_rows(d, -128); scale_data_rows(d, 1./255); smooth_data(d); return d; @@ -1113,6 +1589,19 @@ void translate_data_rows(data d, float s) } } +data copy_data(data d) +{ + data c = {0}; + c.w = d.w; + c.h = d.h; + c.shallow = 0; + c.num_boxes = d.num_boxes; + c.boxes = d.boxes; + c.X = copy_matrix(d.X); + c.y = copy_matrix(d.y); + return c; +} + void normalize_data_rows(data d) { int i; diff --git a/image.darknet/inst/include/darknet/src/data.h b/image.darknet/inst/include/darknet/src/data.h index 3f6ef61..781906f 100644 --- a/image.darknet/inst/include/darknet/src/data.h +++ b/image.darknet/inst/include/darknet/src/data.h @@ -2,6 +2,7 @@ #define DATA_H #include +#include "darknet.h" #include "matrix.h" #include "list.h" #include "image.h" @@ -17,93 +18,32 @@ static inline float distance_from_edge(int x, int max) if (dist > 1) dist = 1; return dist; } +void load_data_blocking(load_args args); -typedef struct{ - int w, h; - matrix X; - matrix y; - int shallow; - int *num_boxes; - box **boxes; -} data; - -typedef enum { - CLASSIFICATION_DATA, DETECTION_DATA, CAPTCHA_DATA, REGION_DATA, IMAGE_DATA, COMPARE_DATA, WRITING_DATA, SWAG_DATA, TAG_DATA, OLD_CLASSIFICATION_DATA, STUDY_DATA, DET_DATA, SUPER_DATA -} data_type; - -typedef struct load_args{ - int threads; - char **paths; - char *path; - int n; - int m; - char **labels; - int h; - int w; - int out_w; - int out_h; - int nh; - int nw; - int num_boxes; - int min, max, size; - int classes; - int background; - int scale; - float jitter; - float angle; - float aspect; - float saturation; - float exposure; - float hue; - data *d; - image *im; - image *resized; - data_type type; - tree *hierarchy; -} load_args; - -typedef struct{ - int id; - float x,y,w,h; - float left, right, top, bottom; -} box_label; - -void free_data(data d); - -pthread_t load_data(load_args args); - -pthread_t load_data_in_thread(load_args args); void print_letters(float *pred, int n); data load_data_captcha(char **paths, int n, int m, int k, int w, int h); data load_data_captcha_encode(char **paths, int n, int m, int w, int h); -data load_data_old(char **paths, int n, int m, char **labels, int k, int w, int h); data load_data_detection(int n, char **paths, int m, int w, int h, int boxes, int classes, float jitter, float hue, float saturation, float exposure); data load_data_tag(char **paths, int n, int m, int k, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure); -matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure); +matrix load_image_augment_paths(char **paths, int n, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure, int center); data load_data_super(char **paths, int n, int m, int w, int h, int scale); -data load_data_augment(char **paths, int n, int m, char **labels, int k, tree *hierarchy, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure); +data load_data_augment(char **paths, int n, int m, char **labels, int k, tree *hierarchy, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure, int center); +data load_data_regression(char **paths, int n, int m, int classes, int min, int max, int size, float angle, float aspect, float hue, float saturation, float exposure); data load_go(char *filename); -box_label *read_boxes(char *filename, int *n); -data load_cifar10_data(char *filename); -data load_all_cifar10(); data load_data_writing(char **paths, int n, int m, int w, int h, int out_w, int out_h); -list *get_paths(char *filename); -char **get_labels(char *filename); void get_random_batch(data d, int n, float *X, float *y); data get_data_part(data d, int part, int total); data get_random_data(data d, int num); -void get_next_batch(data d, int n, int offset, float *X, float *y); data load_categorical_data_csv(char *filename, int target, int k); void normalize_data_rows(data d); void scale_data_rows(data d, float s); void translate_data_rows(data d, float s); void randomize_data(data d); data *split_data(data d, int part, int total); -data concat_data(data d1, data d2); data concat_datas(data *d, int n); void fill_truth(char *path, char **labels, int k, float *truth); diff --git a/image.darknet/inst/include/darknet/src/deconvolutional_kernels.cu b/image.darknet/inst/include/darknet/src/deconvolutional_kernels.cu index d6259fb..8267dcf 100644 --- a/image.darknet/inst/include/darknet/src/deconvolutional_kernels.cu +++ b/image.darknet/inst/include/darknet/src/deconvolutional_kernels.cu @@ -5,6 +5,7 @@ extern "C" { #include "convolutional_layer.h" #include "deconvolutional_layer.h" +#include "batchnorm_layer.h" #include "gemm.h" #include "blas.h" #include "im2col.h" @@ -13,97 +14,126 @@ extern "C" { #include "cuda.h" } -extern "C" void forward_deconvolutional_layer_gpu(deconvolutional_layer layer, network_state state) +extern "C" void forward_deconvolutional_layer_gpu(layer l, network net) { int i; - int out_h = deconvolutional_out_height(layer); - int out_w = deconvolutional_out_width(layer); - int size = out_h*out_w; - int m = layer.size*layer.size*layer.n; - int n = layer.h*layer.w; - int k = layer.c; + int m = l.size*l.size*l.n; + int n = l.h*l.w; + int k = l.c; - fill_ongpu(layer.outputs*layer.batch, 0, layer.output_gpu, 1); + fill_gpu(l.outputs*l.batch, 0, l.output_gpu, 1); - for(i = 0; i < layer.batch; ++i){ - float *a = layer.weights_gpu; - float *b = state.input + i*layer.c*layer.h*layer.w; - float *c = layer.col_image_gpu; + for(i = 0; i < l.batch; ++i){ + float *a = l.weights_gpu; + float *b = net.input_gpu + i*l.c*l.h*l.w; + float *c = net.workspace; - gemm_ongpu(1,0,m,n,k,1,a,m,b,n,0,c,n); + gemm_gpu(1,0,m,n,k,1,a,m,b,n,0,c,n); - col2im_ongpu(c, layer.n, out_h, out_w, layer.size, layer.stride, 0, layer.output_gpu+i*layer.n*size); + col2im_gpu(net.workspace, l.out_c, l.out_h, l.out_w, l.size, l.stride, l.pad, l.output_gpu+i*l.outputs); } - add_bias_gpu(layer.output_gpu, layer.biases_gpu, layer.batch, layer.n, size); - activate_array(layer.output_gpu, layer.batch*layer.n*size, layer.activation); + if (l.batch_normalize) { + forward_batchnorm_layer_gpu(l, net); + } else { + add_bias_gpu(l.output_gpu, l.biases_gpu, l.batch, l.n, l.out_w*l.out_h); + } + activate_array_gpu(l.output_gpu, l.batch*l.n*l.out_w*l.out_h, l.activation); } -extern "C" void backward_deconvolutional_layer_gpu(deconvolutional_layer layer, network_state state) +extern "C" void backward_deconvolutional_layer_gpu(layer l, network net) { - float alpha = 1./layer.batch; - int out_h = deconvolutional_out_height(layer); - int out_w = deconvolutional_out_width(layer); - int size = out_h*out_w; int i; - gradient_array(layer.output_gpu, size*layer.n*layer.batch, layer.activation, layer.delta_gpu); - backward_bias(layer.bias_updates_gpu, layer.delta, layer.batch, layer.n, size); + //constrain_gpu(l.outputs*l.batch, 1, l.delta_gpu, 1); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + + if(l.batch_normalize){ + backward_batchnorm_layer_gpu(l, net); + } else { + backward_bias_gpu(l.bias_updates_gpu, l.delta_gpu, l.batch, l.n, l.out_w*l.out_h); + } - if(state.delta) memset(state.delta, 0, layer.batch*layer.h*layer.w*layer.c*sizeof(float)); + //if(net.delta_gpu) memset(net.delta_gpu, 0, l.batch*l.h*l.w*l.c*sizeof(float)); - for(i = 0; i < layer.batch; ++i){ - int m = layer.c; - int n = layer.size*layer.size*layer.n; - int k = layer.h*layer.w; + for(i = 0; i < l.batch; ++i){ + int m = l.c; + int n = l.size*l.size*l.n; + int k = l.h*l.w; - float *a = state.input + i*m*n; - float *b = layer.col_image_gpu; - float *c = layer.weight_updates_gpu; + float *a = net.input_gpu + i*m*k; + float *b = net.workspace; + float *c = l.weight_updates_gpu; - im2col_ongpu(layer.delta_gpu + i*layer.n*size, layer.n, out_h, out_w, - layer.size, layer.stride, 0, b); - gemm_ongpu(0,1,m,n,k,alpha,a,k,b,k,1,c,n); + im2col_gpu(l.delta_gpu + i*l.outputs, l.out_c, l.out_h, l.out_w, + l.size, l.stride, l.pad, b); + gemm_gpu(0,1,m,n,k,1,a,k,b,k,1,c,n); - if(state.delta){ - int m = layer.c; - int n = layer.h*layer.w; - int k = layer.size*layer.size*layer.n; + if(net.delta_gpu){ + int m = l.c; + int n = l.h*l.w; + int k = l.size*l.size*l.n; - float *a = layer.weights_gpu; - float *b = layer.col_image_gpu; - float *c = state.delta + i*n*m; + float *a = l.weights_gpu; + float *b = net.workspace; + float *c = net.delta_gpu + i*n*m; - gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); + gemm_gpu(0,0,m,n,k,1,a,k,b,n,1,c,n); } } } -extern "C" void pull_deconvolutional_layer(deconvolutional_layer layer) +extern "C" void pull_deconvolutional_layer(layer l) { - cuda_pull_array(layer.weights_gpu, layer.weights, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.biases_gpu, layer.biases, layer.n); - cuda_pull_array(layer.weight_updates_gpu, layer.weight_updates, layer.c*layer.n*layer.size*layer.size); - cuda_pull_array(layer.bias_updates_gpu, layer.bias_updates, layer.n); + cuda_pull_array(l.weights_gpu, l.weights, l.c*l.n*l.size*l.size); + cuda_pull_array(l.biases_gpu, l.biases, l.n); + cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.c*l.n*l.size*l.size); + cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.n); + if (l.batch_normalize){ + cuda_pull_array(l.scales_gpu, l.scales, l.n); + cuda_pull_array(l.rolling_mean_gpu, l.rolling_mean, l.n); + cuda_pull_array(l.rolling_variance_gpu, l.rolling_variance, l.n); + } } -extern "C" void push_deconvolutional_layer(deconvolutional_layer layer) +extern "C" void push_deconvolutional_layer(layer l) { - cuda_push_array(layer.weights_gpu, layer.weights, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.biases_gpu, layer.biases, layer.n); - cuda_push_array(layer.weight_updates_gpu, layer.weight_updates, layer.c*layer.n*layer.size*layer.size); - cuda_push_array(layer.bias_updates_gpu, layer.bias_updates, layer.n); + cuda_push_array(l.weights_gpu, l.weights, l.c*l.n*l.size*l.size); + cuda_push_array(l.biases_gpu, l.biases, l.n); + cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.c*l.n*l.size*l.size); + cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.n); + if (l.batch_normalize){ + cuda_push_array(l.scales_gpu, l.scales, l.n); + cuda_push_array(l.rolling_mean_gpu, l.rolling_mean, l.n); + cuda_push_array(l.rolling_variance_gpu, l.rolling_variance, l.n); + } } -extern "C" void update_deconvolutional_layer_gpu(deconvolutional_layer layer, float learning_rate, float momentum, float decay) +void update_deconvolutional_layer_gpu(layer l, update_args a) { - int size = layer.size*layer.size*layer.c*layer.n; + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + + if(a.adam){ + adam_update_gpu(l.weights_gpu, l.weight_updates_gpu, l.m_gpu, l.v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.nweights, batch, a.t); + adam_update_gpu(l.biases_gpu, l.bias_updates_gpu, l.bias_m_gpu, l.bias_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.n, batch, a.t); + if(l.scales_gpu){ + adam_update_gpu(l.scales_gpu, l.scale_updates_gpu, l.scale_m_gpu, l.scale_v_gpu, a.B1, a.B2, a.eps, decay, learning_rate, l.n, batch, a.t); + } + }else{ + axpy_gpu(l.nweights, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); + axpy_gpu(l.nweights, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); + scal_gpu(l.nweights, momentum, l.weight_updates_gpu, 1); - axpy_ongpu(layer.n, learning_rate, layer.bias_updates_gpu, 1, layer.biases_gpu, 1); - scal_ongpu(layer.n, momentum, layer.bias_updates_gpu, 1); + axpy_gpu(l.n, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); + scal_gpu(l.n, momentum, l.bias_updates_gpu, 1); - axpy_ongpu(size, -decay, layer.weights_gpu, 1, layer.weight_updates_gpu, 1); - axpy_ongpu(size, learning_rate, layer.weight_updates_gpu, 1, layer.weights_gpu, 1); - scal_ongpu(size, momentum, layer.weight_updates_gpu, 1); + if(l.scales_gpu){ + axpy_gpu(l.n, learning_rate/batch, l.scale_updates_gpu, 1, l.scales_gpu, 1); + scal_gpu(l.n, momentum, l.scale_updates_gpu, 1); + } + } } diff --git a/image.darknet/inst/include/darknet/src/deconvolutional_layer.c b/image.darknet/inst/include/darknet/src/deconvolutional_layer.c index fbef9d5..00c0e85 100644 --- a/image.darknet/inst/include/darknet/src/deconvolutional_layer.c +++ b/image.darknet/inst/include/darknet/src/deconvolutional_layer.c @@ -1,52 +1,41 @@ #include "deconvolutional_layer.h" #include "convolutional_layer.h" +#include "batchnorm_layer.h" #include "utils.h" #include "im2col.h" #include "col2im.h" #include "blas.h" #include "gemm.h" + #include #include -int deconvolutional_out_height(deconvolutional_layer l) -{ - int h = l.stride*(l.h - 1) + l.size; - return h; -} -int deconvolutional_out_width(deconvolutional_layer l) -{ - int w = l.stride*(l.w - 1) + l.size; - return w; -} - -int deconvolutional_out_size(deconvolutional_layer l) -{ - return deconvolutional_out_height(l) * deconvolutional_out_width(l); +static size_t get_workspace_size(layer l){ + return (size_t)l.h*l.w*l.size*l.size*l.n*sizeof(float); } -image get_deconvolutional_image(deconvolutional_layer l) +void bilinear_init(layer l) { - int h,w,c; - h = deconvolutional_out_height(l); - w = deconvolutional_out_width(l); - c = l.n; - return float_to_image(w,h,c,l.output); + int i,j,f; + float center = (l.size-1) / 2.; + for(f = 0; f < l.n; ++f){ + for(j = 0; j < l.size; ++j){ + for(i = 0; i < l.size; ++i){ + float val = (1 - fabs(i - center)) * (1 - fabs(j - center)); + int c = f%l.c; + int ind = f*l.size*l.size*l.c + c*l.size*l.size + j*l.size + i; + l.weights[ind] = val; + } + } + } } -image get_deconvolutional_delta(deconvolutional_layer l) -{ - int h,w,c; - h = deconvolutional_out_height(l); - w = deconvolutional_out_width(l); - c = l.n; - return float_to_image(w,h,c,l.delta); -} -deconvolutional_layer make_deconvolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, ACTIVATION activation) +layer make_deconvolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int adam) { int i; - deconvolutional_layer l = {0}; + layer l = {0}; l.type = DECONVOLUTIONAL; l.h = h; @@ -57,82 +46,182 @@ deconvolutional_layer make_deconvolutional_layer(int batch, int h, int w, int c, l.stride = stride; l.size = size; + l.nweights = c*n*size*size; + l.nbiases = n; + l.weights = calloc(c*n*size*size, sizeof(float)); l.weight_updates = calloc(c*n*size*size, sizeof(float)); l.biases = calloc(n, sizeof(float)); l.bias_updates = calloc(n, sizeof(float)); - float scale = 1./sqrt(size*size*c); + //float scale = n/(size*size*c); + //printf("scale: %f\n", scale); + float scale = .02; for(i = 0; i < c*n*size*size; ++i) l.weights[i] = scale*rand_normal(); + //bilinear_init(l); for(i = 0; i < n; ++i){ - l.biases[i] = scale; + l.biases[i] = 0; } - int out_h = deconvolutional_out_height(l); - int out_w = deconvolutional_out_width(l); + l.pad = padding; - l.out_h = out_h; - l.out_w = out_w; + l.out_h = (l.h - 1) * l.stride + l.size - 2*l.pad; + l.out_w = (l.w - 1) * l.stride + l.size - 2*l.pad; l.out_c = n; l.outputs = l.out_w * l.out_h * l.out_c; l.inputs = l.w * l.h * l.c; - l.col_image = calloc(h*w*size*size*n, sizeof(float)); - l.output = calloc(l.batch*out_h * out_w * n, sizeof(float)); - l.delta = calloc(l.batch*out_h * out_w * n, sizeof(float)); + scal_cpu(l.nweights, (float)l.out_w*l.out_h/(l.w*l.h), l.weights, 1); + + l.output = calloc(l.batch*l.outputs, sizeof(float)); + l.delta = calloc(l.batch*l.outputs, sizeof(float)); l.forward = forward_deconvolutional_layer; l.backward = backward_deconvolutional_layer; l.update = update_deconvolutional_layer; - #ifdef GPU - l.weights_gpu = cuda_make_array(l.weights, c*n*size*size); - l.weight_updates_gpu = cuda_make_array(l.weight_updates, c*n*size*size); + l.batch_normalize = batch_normalize; + + if(batch_normalize){ + l.scales = calloc(n, sizeof(float)); + l.scale_updates = calloc(n, sizeof(float)); + for(i = 0; i < n; ++i){ + l.scales[i] = 1; + } + + l.mean = calloc(n, sizeof(float)); + l.variance = calloc(n, sizeof(float)); + + l.mean_delta = calloc(n, sizeof(float)); + l.variance_delta = calloc(n, sizeof(float)); + + l.rolling_mean = calloc(n, sizeof(float)); + l.rolling_variance = calloc(n, sizeof(float)); + l.x = calloc(l.batch*l.outputs, sizeof(float)); + l.x_norm = calloc(l.batch*l.outputs, sizeof(float)); + } + if(adam){ + l.m = calloc(c*n*size*size, sizeof(float)); + l.v = calloc(c*n*size*size, sizeof(float)); + l.bias_m = calloc(n, sizeof(float)); + l.scale_m = calloc(n, sizeof(float)); + l.bias_v = calloc(n, sizeof(float)); + l.scale_v = calloc(n, sizeof(float)); + } + +#ifdef GPU + l.forward_gpu = forward_deconvolutional_layer_gpu; + l.backward_gpu = backward_deconvolutional_layer_gpu; + l.update_gpu = update_deconvolutional_layer_gpu; + + if(gpu_index >= 0){ + + if (adam) { + l.m_gpu = cuda_make_array(l.m, c*n*size*size); + l.v_gpu = cuda_make_array(l.v, c*n*size*size); + l.bias_m_gpu = cuda_make_array(l.bias_m, n); + l.bias_v_gpu = cuda_make_array(l.bias_v, n); + l.scale_m_gpu = cuda_make_array(l.scale_m, n); + l.scale_v_gpu = cuda_make_array(l.scale_v, n); + } + l.weights_gpu = cuda_make_array(l.weights, c*n*size*size); + l.weight_updates_gpu = cuda_make_array(l.weight_updates, c*n*size*size); + + l.biases_gpu = cuda_make_array(l.biases, n); + l.bias_updates_gpu = cuda_make_array(l.bias_updates, n); - l.biases_gpu = cuda_make_array(l.biases, n); - l.bias_updates_gpu = cuda_make_array(l.bias_updates, n); + l.delta_gpu = cuda_make_array(l.delta, l.batch*l.out_h*l.out_w*n); + l.output_gpu = cuda_make_array(l.output, l.batch*l.out_h*l.out_w*n); - l.col_image_gpu = cuda_make_array(l.col_image, h*w*size*size*n); - l.delta_gpu = cuda_make_array(l.delta, l.batch*out_h*out_w*n); - l.output_gpu = cuda_make_array(l.output, l.batch*out_h*out_w*n); + if(batch_normalize){ + l.mean_gpu = cuda_make_array(0, n); + l.variance_gpu = cuda_make_array(0, n); + + l.rolling_mean_gpu = cuda_make_array(0, n); + l.rolling_variance_gpu = cuda_make_array(0, n); + + l.mean_delta_gpu = cuda_make_array(0, n); + l.variance_delta_gpu = cuda_make_array(0, n); + + l.scales_gpu = cuda_make_array(l.scales, n); + l.scale_updates_gpu = cuda_make_array(0, n); + + l.x_gpu = cuda_make_array(0, l.batch*l.out_h*l.out_w*n); + l.x_norm_gpu = cuda_make_array(0, l.batch*l.out_h*l.out_w*n); + } + } + #ifdef CUDNN + cudnnCreateTensorDescriptor(&l.dstTensorDesc); + cudnnCreateTensorDescriptor(&l.normTensorDesc); + cudnnSetTensor4dDescriptor(l.dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l.batch, l.out_c, l.out_h, l.out_w); + cudnnSetTensor4dDescriptor(l.normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l.out_c, 1, 1); #endif +#endif l.activation = activation; + l.workspace_size = get_workspace_size(l); - fprintf(stderr, "Deconvolutional Layer: %d x %d x %d image, %d filters -> %d x %d x %d image\n", h,w,c,n, out_h, out_w, n); + fprintf(stderr, "deconv%5d %2d x%2d /%2d %4d x%4d x%4d -> %4d x%4d x%4d\n", n, size, size, stride, w, h, c, l.out_w, l.out_h, l.out_c); return l; } -void resize_deconvolutional_layer(deconvolutional_layer *l, int h, int w) +void denormalize_deconvolutional_layer(layer l) +{ + int i, j; + for(i = 0; i < l.n; ++i){ + float scale = l.scales[i]/sqrt(l.rolling_variance[i] + .00001); + for(j = 0; j < l.c*l.size*l.size; ++j){ + l.weights[i*l.c*l.size*l.size + j] *= scale; + } + l.biases[i] -= l.rolling_mean[i] * scale; + l.scales[i] = 1; + l.rolling_mean[i] = 0; + l.rolling_variance[i] = 1; + } +} + +void resize_deconvolutional_layer(layer *l, int h, int w) { l->h = h; l->w = w; - int out_h = deconvolutional_out_height(*l); - int out_w = deconvolutional_out_width(*l); - - l->col_image = realloc(l->col_image, - out_h*out_w*l->size*l->size*l->c*sizeof(float)); - l->output = realloc(l->output, - l->batch*out_h * out_w * l->n*sizeof(float)); - l->delta = realloc(l->delta, - l->batch*out_h * out_w * l->n*sizeof(float)); - #ifdef GPU - cuda_free(l->col_image_gpu); + l->out_h = (l->h - 1) * l->stride + l->size - 2*l->pad; + l->out_w = (l->w - 1) * l->stride + l->size - 2*l->pad; + + l->outputs = l->out_h * l->out_w * l->out_c; + l->inputs = l->w * l->h * l->c; + + l->output = realloc(l->output, l->batch*l->outputs*sizeof(float)); + l->delta = realloc(l->delta, l->batch*l->outputs*sizeof(float)); + if(l->batch_normalize){ + l->x = realloc(l->x, l->batch*l->outputs*sizeof(float)); + l->x_norm = realloc(l->x_norm, l->batch*l->outputs*sizeof(float)); + } + +#ifdef GPU cuda_free(l->delta_gpu); cuda_free(l->output_gpu); - l->col_image_gpu = cuda_make_array(l->col_image, out_h*out_w*l->size*l->size*l->c); - l->delta_gpu = cuda_make_array(l->delta, l->batch*out_h*out_w*l->n); - l->output_gpu = cuda_make_array(l->output, l->batch*out_h*out_w*l->n); + l->delta_gpu = cuda_make_array(l->delta, l->batch*l->outputs); + l->output_gpu = cuda_make_array(l->output, l->batch*l->outputs); + + if(l->batch_normalize){ + cuda_free(l->x_gpu); + cuda_free(l->x_norm_gpu); + + l->x_gpu = cuda_make_array(l->output, l->batch*l->outputs); + l->x_norm_gpu = cuda_make_array(l->output, l->batch*l->outputs); + } + #ifdef CUDNN + cudnnSetTensor4dDescriptor(l->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, l->batch, l->out_c, l->out_h, l->out_w); + cudnnSetTensor4dDescriptor(l->normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l->out_c, 1, 1); #endif +#endif + l->workspace_size = get_workspace_size(*l); } -void forward_deconvolutional_layer(const deconvolutional_layer l, network_state state) +void forward_deconvolutional_layer(const layer l, network net) { int i; - int out_h = deconvolutional_out_height(l); - int out_w = deconvolutional_out_width(l); - int size = out_h*out_w; int m = l.size*l.size*l.n; int n = l.h*l.w; @@ -142,63 +231,80 @@ void forward_deconvolutional_layer(const deconvolutional_layer l, network_state for(i = 0; i < l.batch; ++i){ float *a = l.weights; - float *b = state.input + i*l.c*l.h*l.w; - float *c = l.col_image; + float *b = net.input + i*l.c*l.h*l.w; + float *c = net.workspace; - gemm(1,0,m,n,k,1,a,m,b,n,0,c,n); + gemm_cpu(1,0,m,n,k,1,a,m,b,n,0,c,n); - col2im_cpu(c, l.n, out_h, out_w, l.size, l.stride, 0, l.output+i*l.n*size); + col2im_cpu(net.workspace, l.out_c, l.out_h, l.out_w, l.size, l.stride, l.pad, l.output+i*l.outputs); + } + if (l.batch_normalize) { + forward_batchnorm_layer(l, net); + } else { + add_bias(l.output, l.biases, l.batch, l.n, l.out_w*l.out_h); } - add_bias(l.output, l.biases, l.batch, l.n, size); - activate_array(l.output, l.batch*l.n*size, l.activation); + activate_array(l.output, l.batch*l.n*l.out_w*l.out_h, l.activation); } -void backward_deconvolutional_layer(deconvolutional_layer l, network_state state) +void backward_deconvolutional_layer(layer l, network net) { - float alpha = 1./l.batch; - int out_h = deconvolutional_out_height(l); - int out_w = deconvolutional_out_width(l); - int size = out_h*out_w; int i; - gradient_array(l.output, size*l.n*l.batch, l.activation, l.delta); - backward_bias(l.bias_updates, l.delta, l.batch, l.n, size); + gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); + + if(l.batch_normalize){ + backward_batchnorm_layer(l, net); + } else { + backward_bias(l.bias_updates, l.delta, l.batch, l.n, l.out_w*l.out_h); + } + + //if(net.delta) memset(net.delta, 0, l.batch*l.h*l.w*l.c*sizeof(float)); for(i = 0; i < l.batch; ++i){ int m = l.c; int n = l.size*l.size*l.n; int k = l.h*l.w; - float *a = state.input + i*m*n; - float *b = l.col_image; + float *a = net.input + i*m*k; + float *b = net.workspace; float *c = l.weight_updates; - im2col_cpu(l.delta + i*l.n*size, l.n, out_h, out_w, - l.size, l.stride, 0, b); - gemm(0,1,m,n,k,alpha,a,k,b,k,1,c,n); + im2col_cpu(l.delta + i*l.outputs, l.out_c, l.out_h, l.out_w, + l.size, l.stride, l.pad, b); + gemm_cpu(0,1,m,n,k,1,a,k,b,k,1,c,n); - if(state.delta){ + if(net.delta){ int m = l.c; int n = l.h*l.w; int k = l.size*l.size*l.n; float *a = l.weights; - float *b = l.col_image; - float *c = state.delta + i*n*m; + float *b = net.workspace; + float *c = net.delta + i*n*m; - gemm(0,0,m,n,k,1,a,k,b,n,1,c,n); + gemm_cpu(0,0,m,n,k,1,a,k,b,n,1,c,n); } } } -void update_deconvolutional_layer(deconvolutional_layer l, float learning_rate, float momentum, float decay) +void update_deconvolutional_layer(layer l, update_args a) { + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + int size = l.size*l.size*l.c*l.n; - axpy_cpu(l.n, learning_rate, l.bias_updates, 1, l.biases, 1); + axpy_cpu(l.n, learning_rate/batch, l.bias_updates, 1, l.biases, 1); scal_cpu(l.n, momentum, l.bias_updates, 1); - axpy_cpu(size, -decay, l.weights, 1, l.weight_updates, 1); - axpy_cpu(size, learning_rate, l.weight_updates, 1, l.weights, 1); + if(l.scales){ + axpy_cpu(l.n, learning_rate/batch, l.scale_updates, 1, l.scales, 1); + scal_cpu(l.n, momentum, l.scale_updates, 1); + } + + axpy_cpu(size, -decay*batch, l.weights, 1, l.weight_updates, 1); + axpy_cpu(size, learning_rate/batch, l.weight_updates, 1, l.weights, 1); scal_cpu(size, momentum, l.weight_updates, 1); } diff --git a/image.darknet/inst/include/darknet/src/deconvolutional_layer.h b/image.darknet/inst/include/darknet/src/deconvolutional_layer.h index 2d36e02..b254fb9 100644 --- a/image.darknet/inst/include/darknet/src/deconvolutional_layer.h +++ b/image.darknet/inst/include/darknet/src/deconvolutional_layer.h @@ -7,28 +7,19 @@ #include "layer.h" #include "network.h" -typedef layer deconvolutional_layer; - #ifdef GPU -void forward_deconvolutional_layer_gpu(deconvolutional_layer layer, network_state state); -void backward_deconvolutional_layer_gpu(deconvolutional_layer layer, network_state state); -void update_deconvolutional_layer_gpu(deconvolutional_layer layer, float learning_rate, float momentum, float decay); -void push_deconvolutional_layer(deconvolutional_layer layer); -void pull_deconvolutional_layer(deconvolutional_layer layer); +void forward_deconvolutional_layer_gpu(layer l, network net); +void backward_deconvolutional_layer_gpu(layer l, network net); +void update_deconvolutional_layer_gpu(layer l, update_args a); +void push_deconvolutional_layer(layer l); +void pull_deconvolutional_layer(layer l); #endif -deconvolutional_layer make_deconvolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, ACTIVATION activation); -void resize_deconvolutional_layer(deconvolutional_layer *layer, int h, int w); -void forward_deconvolutional_layer(const deconvolutional_layer layer, network_state state); -void update_deconvolutional_layer(deconvolutional_layer layer, float learning_rate, float momentum, float decay); -void backward_deconvolutional_layer(deconvolutional_layer layer, network_state state); - -image get_deconvolutional_image(deconvolutional_layer layer); -image get_deconvolutional_delta(deconvolutional_layer layer); -image get_deconvolutional_filter(deconvolutional_layer layer, int i); - -int deconvolutional_out_height(deconvolutional_layer layer); -int deconvolutional_out_width(deconvolutional_layer layer); +layer make_deconvolutional_layer(int batch, int h, int w, int c, int n, int size, int stride, int padding, ACTIVATION activation, int batch_normalize, int adam); +void resize_deconvolutional_layer(layer *l, int h, int w); +void forward_deconvolutional_layer(const layer l, network net); +void update_deconvolutional_layer(layer l, update_args a); +void backward_deconvolutional_layer(layer l, network net); #endif diff --git a/image.darknet/inst/include/darknet/src/demo.c b/image.darknet/inst/include/darknet/src/demo.c index 7818bc3..b89efb8 100644 --- a/image.darknet/inst/include/darknet/src/demo.c +++ b/image.darknet/inst/include/darknet/src/demo.c @@ -9,213 +9,339 @@ #include "demo.h" #include -#define FRAMES 3 +#define DEMO 1 #ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#include "opencv2/imgproc/imgproc_c.h" -image get_image_from_stream(CvCapture *cap); static char **demo_names; static image **demo_alphabet; static int demo_classes; -static float **probs; -static box *boxes; -static network net; -static image in ; -static image in_s ; -static image det ; -static image det_s; -static image disp = {0}; -static CvCapture * cap; +static network *net; +static image buff [3]; +static image buff_letter[3]; +static int buff_index = 0; +static void * cap; static float fps = 0; static float demo_thresh = 0; -static float demo_hier_thresh = .5; +static float demo_hier = .5; +static int running = 0; -static float *predictions[FRAMES]; +static int demo_frame = 3; static int demo_index = 0; -static image images[FRAMES]; +static float **predictions; static float *avg; +static int demo_done = 0; +static int demo_total = 0; +double demo_time; -void *fetch_in_thread(void *ptr) +detection *get_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, int *num); + +int size_network(network *net) { - in = get_image_from_stream(cap); - if(!in.data){ - error("Stream closed."); + int i; + int count = 0; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; + if(l.type == YOLO || l.type == REGION || l.type == DETECTION){ + count += l.outputs; + } } - in_s = resize_image(in, net.w, net.h); - return 0; + return count; +} + +void remember_network(network *net) +{ + int i; + int count = 0; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; + if(l.type == YOLO || l.type == REGION || l.type == DETECTION){ + memcpy(predictions[demo_index] + count, net->layers[i].output, sizeof(float) * l.outputs); + count += l.outputs; + } + } +} + +detection *avg_predictions(network *net, int *nboxes) +{ + int i, j; + int count = 0; + fill_cpu(demo_total, 0, avg, 1); + for(j = 0; j < demo_frame; ++j){ + axpy_cpu(demo_total, 1./demo_frame, predictions[j], 1, avg, 1); + } + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; + if(l.type == YOLO || l.type == REGION || l.type == DETECTION){ + memcpy(l.output, avg + count, sizeof(float) * l.outputs); + count += l.outputs; + } + } + detection *dets = get_network_boxes(net, buff[0].w, buff[0].h, demo_thresh, demo_hier, 0, 1, nboxes); + return dets; } void *detect_in_thread(void *ptr) { + running = 1; float nms = .4; - layer l = net.layers[net.n-1]; - float *X = det_s.data; - float *prediction = network_predict(net, X); - - memcpy(predictions[demo_index], prediction, l.outputs*sizeof(float)); - mean_arrays(predictions, FRAMES, l.outputs, avg); - l.output = avg; - - free_image(det_s); - if(l.type == DETECTION){ - get_detection_boxes(l, 1, 1, demo_thresh, probs, boxes, 0); - } else if (l.type == REGION){ - get_region_boxes(l, 1, 1, demo_thresh, probs, boxes, 0, 0, demo_hier_thresh); - } else { - error("Last layer must produce detections\n"); + layer l = net->layers[net->n-1]; + float *X = buff_letter[(buff_index+2)%3].data; + network_predict(net, X); + + /* + if(l.type == DETECTION){ + get_detection_boxes(l, 1, 1, demo_thresh, probs, boxes, 0); + } else */ + remember_network(net); + detection *dets = 0; + int nboxes = 0; + dets = avg_predictions(net, &nboxes); + + + /* + int i,j; + box zero = {0}; + int classes = l.classes; + for(i = 0; i < demo_detections; ++i){ + avg[i].objectness = 0; + avg[i].bbox = zero; + memset(avg[i].prob, 0, classes*sizeof(float)); + for(j = 0; j < demo_frame; ++j){ + axpy_cpu(classes, 1./demo_frame, dets[j][i].prob, 1, avg[i].prob, 1); + avg[i].objectness += dets[j][i].objectness * 1./demo_frame; + avg[i].bbox.x += dets[j][i].bbox.x * 1./demo_frame; + avg[i].bbox.y += dets[j][i].bbox.y * 1./demo_frame; + avg[i].bbox.w += dets[j][i].bbox.w * 1./demo_frame; + avg[i].bbox.h += dets[j][i].bbox.h * 1./demo_frame; + } + //copy_cpu(classes, dets[0][i].prob, 1, avg[i].prob, 1); + //avg[i].objectness = dets[0][i].objectness; } - if (nms > 0) do_nms(boxes, probs, l.w*l.h*l.n, l.classes, nms); + */ + + if (nms > 0) do_nms_obj(dets, nboxes, l.classes, nms); + printf("\033[2J"); printf("\033[1;1H"); printf("\nFPS:%.1f\n",fps); printf("Objects:\n\n"); + image display = buff[(buff_index+2) % 3]; + draw_detections(display, dets, nboxes, demo_thresh, demo_names, demo_alphabet, demo_classes); + free_detections(dets, nboxes); - images[demo_index] = det; - det = images[(demo_index + FRAMES/2 + 1)%FRAMES]; - demo_index = (demo_index + 1)%FRAMES; - - draw_detections(det, l.w*l.h*l.n, demo_thresh, boxes, probs, demo_names, demo_alphabet, demo_classes); + demo_index = (demo_index + 1)%demo_frame; + running = 0; + return 0; +} +void *fetch_in_thread(void *ptr) +{ + free_image(buff[buff_index]); + buff[buff_index] = get_image_from_stream(cap); + if(buff[buff_index].data == 0) { + demo_done = 1; + return 0; + } + letterbox_image_into(buff[buff_index], net->w, net->h, buff_letter[buff_index]); return 0; } -double get_wall_time() +void *display_in_thread(void *ptr) { - struct timeval time; - if (gettimeofday(&time,NULL)){ + int c = show_image(buff[(buff_index + 1)%3], "Demo", 1); + if (c != -1) c = c%256; + if (c == 27) { + demo_done = 1; return 0; + } else if (c == 82) { + demo_thresh += .02; + } else if (c == 84) { + demo_thresh -= .02; + if(demo_thresh <= .02) demo_thresh = .02; + } else if (c == 83) { + demo_hier += .02; + } else if (c == 81) { + demo_hier -= .02; + if(demo_hier <= .0) demo_hier = .0; } - return (double)time.tv_sec + (double)time.tv_usec * .000001; + return 0; } -void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int frame_skip, char *prefix, float hier_thresh) +void *display_loop(void *ptr) { - //skip = frame_skip; + while(1){ + display_in_thread(0); + } +} + +void *detect_loop(void *ptr) +{ + while(1){ + detect_in_thread(0); + } +} + +void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int delay, char *prefix, int avg_frames, float hier, int w, int h, int frames, int fullscreen) +{ + //demo_frame = avg_frames; image **alphabet = load_alphabet(); - int delay = frame_skip; demo_names = names; demo_alphabet = alphabet; demo_classes = classes; demo_thresh = thresh; - demo_hier_thresh = hier_thresh; + demo_hier = hier; printf("Demo\n"); - net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); + pthread_t detect_thread; + pthread_t fetch_thread; srand(2222222); + int i; + demo_total = size_network(net); + predictions = calloc(demo_frame, sizeof(float*)); + for (i = 0; i < demo_frame; ++i){ + predictions[i] = calloc(demo_total, sizeof(float)); + } + avg = calloc(demo_total, sizeof(float)); + if(filename){ printf("video file: %s\n", filename); - cap = cvCaptureFromFile(filename); + cap = open_video_stream(filename, 0, 0, 0, 0); }else{ - cap = cvCaptureFromCAM(cam_index); + cap = open_video_stream(0, cam_index, w, h, frames); } if(!cap) error("Couldn't connect to webcam.\n"); - layer l = net.layers[net.n-1]; - int j; - - avg = (float *) calloc(l.outputs, sizeof(float)); - for(j = 0; j < FRAMES; ++j) predictions[j] = (float *) calloc(l.outputs, sizeof(float)); - for(j = 0; j < FRAMES; ++j) images[j] = make_image(1,1,3); - - boxes = (box *)calloc(l.w*l.h*l.n, sizeof(box)); - probs = (float **)calloc(l.w*l.h*l.n, sizeof(float *)); - for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = (float *)calloc(l.classes, sizeof(float)); - - pthread_t fetch_thread; - pthread_t detect_thread; - - fetch_in_thread(0); - det = in; - det_s = in_s; - - fetch_in_thread(0); - detect_in_thread(0); - disp = det; - det = in; - det_s = in_s; - - for(j = 0; j < FRAMES/2; ++j){ - fetch_in_thread(0); - detect_in_thread(0); - disp = det; - det = in; - det_s = in_s; - } + buff[0] = get_image_from_stream(cap); + buff[1] = copy_image(buff[0]); + buff[2] = copy_image(buff[0]); + buff_letter[0] = letterbox_image(buff[0], net->w, net->h); + buff_letter[1] = letterbox_image(buff[0], net->w, net->h); + buff_letter[2] = letterbox_image(buff[0], net->w, net->h); int count = 0; if(!prefix){ - cvNamedWindow("Demo", CV_WINDOW_NORMAL); - cvMoveWindow("Demo", 0, 0); - cvResizeWindow("Demo", 1352, 1013); + make_window("Demo", 1352, 1013, fullscreen); } - double before = get_wall_time(); - - while(1){ - ++count; - if(1){ - if(pthread_create(&fetch_thread, 0, fetch_in_thread, 0)) error("Thread creation failed"); - if(pthread_create(&detect_thread, 0, detect_in_thread, 0)) error("Thread creation failed"); - - if(!prefix){ - show_image(disp, "Demo"); - int c = cvWaitKey(1); - if (c == 10){ - if(frame_skip == 0) frame_skip = 60; - else if(frame_skip == 4) frame_skip = 0; - else if(frame_skip == 60) frame_skip = 4; - else frame_skip = 0; - } - }else{ - char buff[256]; - sprintf(buff, "%s_%08d", prefix, count); - save_image(disp, buff); - } - - pthread_join(fetch_thread, 0); - pthread_join(detect_thread, 0); - - if(delay == 0){ - free_image(disp); - disp = det; - } - det = in; - det_s = in_s; - }else { - fetch_in_thread(0); - det = in; - det_s = in_s; - detect_in_thread(0); - if(delay == 0) { - free_image(disp); - disp = det; - } - show_image(disp, "Demo"); - cvWaitKey(1); - } - --delay; - if(delay < 0){ - delay = frame_skip; - - double after = get_wall_time(); - float curr = 1./(after - before); - fps = curr; - before = after; + demo_time = what_time_is_it_now(); + + while(!demo_done){ + buff_index = (buff_index + 1) %3; + if(pthread_create(&fetch_thread, 0, fetch_in_thread, 0)) error("Thread creation failed"); + if(pthread_create(&detect_thread, 0, detect_in_thread, 0)) error("Thread creation failed"); + if(!prefix){ + fps = 1./(what_time_is_it_now() - demo_time); + demo_time = what_time_is_it_now(); + display_in_thread(0); + }else{ + char name[256]; + sprintf(name, "%s_%08d", prefix, count); + save_image(buff[(buff_index + 1)%3], name); } + pthread_join(fetch_thread, 0); + pthread_join(detect_thread, 0); + ++count; } } + +/* + void demo_compare(char *cfg1, char *weight1, char *cfg2, char *weight2, float thresh, int cam_index, const char *filename, char **names, int classes, int delay, char *prefix, int avg_frames, float hier, int w, int h, int frames, int fullscreen) + { + demo_frame = avg_frames; + predictions = calloc(demo_frame, sizeof(float*)); + image **alphabet = load_alphabet(); + demo_names = names; + demo_alphabet = alphabet; + demo_classes = classes; + demo_thresh = thresh; + demo_hier = hier; + printf("Demo\n"); + net = load_network(cfg1, weight1, 0); + set_batch_network(net, 1); + pthread_t detect_thread; + pthread_t fetch_thread; + + srand(2222222); + + if(filename){ + printf("video file: %s\n", filename); + cap = cvCaptureFromFile(filename); + }else{ + cap = cvCaptureFromCAM(cam_index); + + if(w){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FRAME_WIDTH, w); + } + if(h){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FRAME_HEIGHT, h); + } + if(frames){ + cvSetCaptureProperty(cap, CV_CAP_PROP_FPS, frames); + } + } + + if(!cap) error("Couldn't connect to webcam.\n"); + + layer l = net->layers[net->n-1]; + demo_detections = l.n*l.w*l.h; + int j; + + avg = (float *) calloc(l.outputs, sizeof(float)); + for(j = 0; j < demo_frame; ++j) predictions[j] = (float *) calloc(l.outputs, sizeof(float)); + + boxes = (box *)calloc(l.w*l.h*l.n, sizeof(box)); + probs = (float **)calloc(l.w*l.h*l.n, sizeof(float *)); + for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = (float *)calloc(l.classes+1, sizeof(float)); + + buff[0] = get_image_from_stream(cap); + buff[1] = copy_image(buff[0]); + buff[2] = copy_image(buff[0]); + buff_letter[0] = letterbox_image(buff[0], net->w, net->h); + buff_letter[1] = letterbox_image(buff[0], net->w, net->h); + buff_letter[2] = letterbox_image(buff[0], net->w, net->h); + ipl = cvCreateImage(cvSize(buff[0].w,buff[0].h), IPL_DEPTH_8U, buff[0].c); + + int count = 0; + if(!prefix){ + cvNamedWindow("Demo", CV_WINDOW_NORMAL); + if(fullscreen){ + cvSetWindowProperty("Demo", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN); + } else { + cvMoveWindow("Demo", 0, 0); + cvResizeWindow("Demo", 1352, 1013); + } + } + + demo_time = what_time_is_it_now(); + + while(!demo_done){ +buff_index = (buff_index + 1) %3; +if(pthread_create(&fetch_thread, 0, fetch_in_thread, 0)) error("Thread creation failed"); +if(pthread_create(&detect_thread, 0, detect_in_thread, 0)) error("Thread creation failed"); +if(!prefix){ + fps = 1./(what_time_is_it_now() - demo_time); + demo_time = what_time_is_it_now(); + display_in_thread(0); +}else{ + char name[256]; + sprintf(name, "%s_%08d", prefix, count); + save_image(buff[(buff_index + 1)%3], name); +} +pthread_join(fetch_thread, 0); +pthread_join(detect_thread, 0); +++count; +} +} +*/ #else -void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int frame_skip, char *prefix, float hier_thresh) +void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int delay, char *prefix, int avg, float hier, int w, int h, int frames, int fullscreen) { fprintf(stderr, "Demo needs OpenCV for webcam images.\n"); } diff --git a/image.darknet/inst/include/darknet/src/demo.h b/image.darknet/inst/include/darknet/src/demo.h index c3d6a61..86e4654 100644 --- a/image.darknet/inst/include/darknet/src/demo.h +++ b/image.darknet/inst/include/darknet/src/demo.h @@ -1,7 +1,6 @@ -#ifndef DEMO -#define DEMO +#ifndef DEMO_H +#define DEMO_H #include "image.h" -void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int frame_skip, char *prefix, float hier_thresh); #endif diff --git a/image.darknet/inst/include/darknet/src/detection_layer.c b/image.darknet/inst/include/darknet/src/detection_layer.c index cd98b4b..d0e0194 100644 --- a/image.darknet/inst/include/darknet/src/detection_layer.c +++ b/image.darknet/inst/include/darknet/src/detection_layer.c @@ -5,6 +5,7 @@ #include "box.h" #include "cuda.h" #include "utils.h" + #include #include #include @@ -46,11 +47,11 @@ detection_layer make_detection_layer(int batch, int inputs, int n, int side, int return l; } -void forward_detection_layer(const detection_layer l, network_state state) +void forward_detection_layer(const detection_layer l, network net) { int locations = l.side*l.side; int i,j; - memcpy(l.output, state.input, l.outputs*l.batch*sizeof(float)); + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); //if(l.reorg) reorg(l.output, l.w*l.h, size*l.n, l.batch, 1); int b; if (l.softmax){ @@ -58,12 +59,12 @@ void forward_detection_layer(const detection_layer l, network_state state) int index = b*l.inputs; for (i = 0; i < locations; ++i) { int offset = i*l.classes; - softmax(l.output + index + offset, l.classes, 1, + softmax(l.output + index + offset, l.classes, 1, 1, l.output + index + offset); } } } - if(state.train){ + if(net.train){ float avg_iou = 0; float avg_cat = 0; float avg_allcat = 0; @@ -77,7 +78,7 @@ void forward_detection_layer(const detection_layer l, network_state state) int index = b*l.inputs; for (i = 0; i < locations; ++i) { int truth_index = (b*locations + i)*(1+l.coords+l.classes); - int is_obj = state.truth[truth_index]; + int is_obj = net.truth[truth_index]; for (j = 0; j < l.n; ++j) { int p_index = index + locations*l.classes + i*l.n + j; l.delta[p_index] = l.noobject_scale*(0 - l.output[p_index]); @@ -95,19 +96,19 @@ void forward_detection_layer(const detection_layer l, network_state state) int class_index = index + i*l.classes; for(j = 0; j < l.classes; ++j) { - l.delta[class_index+j] = l.class_scale * (state.truth[truth_index+1+j] - l.output[class_index+j]); - *(l.cost) += l.class_scale * pow(state.truth[truth_index+1+j] - l.output[class_index+j], 2); - if(state.truth[truth_index + 1 + j]) avg_cat += l.output[class_index+j]; + l.delta[class_index+j] = l.class_scale * (net.truth[truth_index+1+j] - l.output[class_index+j]); + *(l.cost) += l.class_scale * pow(net.truth[truth_index+1+j] - l.output[class_index+j], 2); + if(net.truth[truth_index + 1 + j]) avg_cat += l.output[class_index+j]; avg_allcat += l.output[class_index+j]; } - box truth = float_to_box(state.truth + truth_index + 1 + l.classes); + box truth = float_to_box(net.truth + truth_index + 1 + l.classes, 1); truth.x /= l.side; truth.y /= l.side; for(j = 0; j < l.n; ++j){ int box_index = index + locations*(l.classes + l.n) + (i*l.n + j) * l.coords; - box out = float_to_box(l.output + box_index); + box out = float_to_box(l.output + box_index, 1); out.x /= l.side; out.y /= l.side; @@ -139,14 +140,14 @@ void forward_detection_layer(const detection_layer l, network_state state) best_index = 0; } } - if(l.random && *(state.net.seen) < 64000){ + if(l.random && *(net.seen) < 64000){ best_index = rand()%l.n; } int box_index = index + locations*(l.classes + l.n) + (i*l.n + best_index) * l.coords; int tbox_index = truth_index + 1 + l.classes; - box out = float_to_box(l.output + box_index); + box out = float_to_box(l.output + box_index, 1); out.x /= l.side; out.y /= l.side; if (l.sqrt) { @@ -166,13 +167,13 @@ void forward_detection_layer(const detection_layer l, network_state state) l.delta[p_index] = l.object_scale * (iou - l.output[p_index]); } - l.delta[box_index+0] = l.coord_scale*(state.truth[tbox_index + 0] - l.output[box_index + 0]); - l.delta[box_index+1] = l.coord_scale*(state.truth[tbox_index + 1] - l.output[box_index + 1]); - l.delta[box_index+2] = l.coord_scale*(state.truth[tbox_index + 2] - l.output[box_index + 2]); - l.delta[box_index+3] = l.coord_scale*(state.truth[tbox_index + 3] - l.output[box_index + 3]); + l.delta[box_index+0] = l.coord_scale*(net.truth[tbox_index + 0] - l.output[box_index + 0]); + l.delta[box_index+1] = l.coord_scale*(net.truth[tbox_index + 1] - l.output[box_index + 1]); + l.delta[box_index+2] = l.coord_scale*(net.truth[tbox_index + 2] - l.output[box_index + 2]); + l.delta[box_index+3] = l.coord_scale*(net.truth[tbox_index + 3] - l.output[box_index + 3]); if(l.sqrt){ - l.delta[box_index+2] = l.coord_scale*(sqrt(state.truth[tbox_index + 2]) - l.output[box_index + 2]); - l.delta[box_index+3] = l.coord_scale*(sqrt(state.truth[tbox_index + 3]) - l.output[box_index + 3]); + l.delta[box_index+2] = l.coord_scale*(sqrt(net.truth[tbox_index + 2]) - l.output[box_index + 2]); + l.delta[box_index+3] = l.coord_scale*(sqrt(net.truth[tbox_index + 3]) - l.output[box_index + 3]); } *(l.cost) += pow(1-iou, 2); @@ -216,12 +217,12 @@ void forward_detection_layer(const detection_layer l, network_state state) } } -void backward_detection_layer(const detection_layer l, network_state state) +void backward_detection_layer(const detection_layer l, network net) { - axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, state.delta, 1); + axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, net.delta, 1); } -void get_detection_boxes(layer l, int w, int h, float thresh, float **probs, box *boxes, int only_objectness) +void get_detection_detections(layer l, int w, int h, float thresh, detection *dets) { int i,j,n; float *predictions = l.output; @@ -234,17 +235,17 @@ void get_detection_boxes(layer l, int w, int h, float thresh, float **probs, box int p_index = l.side*l.side*l.classes + i*l.n + n; float scale = predictions[p_index]; int box_index = l.side*l.side*(l.classes + l.n) + (i*l.n + n)*4; - boxes[index].x = (predictions[box_index + 0] + col) / l.side * w; - boxes[index].y = (predictions[box_index + 1] + row) / l.side * h; - boxes[index].w = pow(predictions[box_index + 2], (l.sqrt?2:1)) * w; - boxes[index].h = pow(predictions[box_index + 3], (l.sqrt?2:1)) * h; + box b; + b.x = (predictions[box_index + 0] + col) / l.side * w; + b.y = (predictions[box_index + 1] + row) / l.side * h; + b.w = pow(predictions[box_index + 2], (l.sqrt?2:1)) * w; + b.h = pow(predictions[box_index + 3], (l.sqrt?2:1)) * h; + dets[index].bbox = b; + dets[index].objectness = scale; for(j = 0; j < l.classes; ++j){ int class_index = i*l.classes; float prob = scale*predictions[class_index+j]; - probs[index][j] = (prob > thresh) ? prob : 0; - } - if(only_objectness){ - probs[index][0] = scale; + dets[index].prob[j] = (prob > thresh) ? prob : 0; } } } @@ -252,36 +253,23 @@ void get_detection_boxes(layer l, int w, int h, float thresh, float **probs, box #ifdef GPU -void forward_detection_layer_gpu(const detection_layer l, network_state state) +void forward_detection_layer_gpu(const detection_layer l, network net) { - if(!state.train){ - copy_ongpu(l.batch*l.inputs, state.input, 1, l.output_gpu, 1); + if(!net.train){ + copy_gpu(l.batch*l.inputs, net.input_gpu, 1, l.output_gpu, 1); return; } - float *in_cpu = calloc(l.batch*l.inputs, sizeof(float)); - float *truth_cpu = 0; - if(state.truth){ - int num_truth = l.batch*l.side*l.side*(1+l.coords+l.classes); - truth_cpu = calloc(num_truth, sizeof(float)); - cuda_pull_array(state.truth, truth_cpu, num_truth); - } - cuda_pull_array(state.input, in_cpu, l.batch*l.inputs); - network_state cpu_state = state; - cpu_state.train = state.train; - cpu_state.truth = truth_cpu; - cpu_state.input = in_cpu; - forward_detection_layer(l, cpu_state); + cuda_pull_array(net.input_gpu, net.input, l.batch*l.inputs); + forward_detection_layer(l, net); cuda_push_array(l.output_gpu, l.output, l.batch*l.outputs); cuda_push_array(l.delta_gpu, l.delta, l.batch*l.inputs); - free(cpu_state.input); - if(cpu_state.truth) free(cpu_state.truth); } -void backward_detection_layer_gpu(detection_layer l, network_state state) +void backward_detection_layer_gpu(detection_layer l, network net) { - axpy_ongpu(l.batch*l.inputs, 1, l.delta_gpu, 1, state.delta, 1); - //copy_ongpu(l.batch*l.inputs, l.delta_gpu, 1, state.delta, 1); + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); + //copy_gpu(l.batch*l.inputs, l.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/inst/include/darknet/src/detection_layer.h b/image.darknet/inst/include/darknet/src/detection_layer.h index e847a09..1c81853 100644 --- a/image.darknet/inst/include/darknet/src/detection_layer.h +++ b/image.darknet/inst/include/darknet/src/detection_layer.h @@ -7,13 +7,12 @@ typedef layer detection_layer; detection_layer make_detection_layer(int batch, int inputs, int n, int size, int classes, int coords, int rescore); -void forward_detection_layer(const detection_layer l, network_state state); -void backward_detection_layer(const detection_layer l, network_state state); -void get_detection_boxes(layer l, int w, int h, float thresh, float **probs, box *boxes, int only_objectness); +void forward_detection_layer(const detection_layer l, network net); +void backward_detection_layer(const detection_layer l, network net); #ifdef GPU -void forward_detection_layer_gpu(const detection_layer l, network_state state); -void backward_detection_layer_gpu(detection_layer l, network_state state); +void forward_detection_layer_gpu(const detection_layer l, network net); +void backward_detection_layer_gpu(detection_layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/detector.c b/image.darknet/inst/include/darknet/src/detector.c deleted file mode 100644 index 1416c05..0000000 --- a/image.darknet/inst/include/darknet/src/detector.c +++ /dev/null @@ -1,552 +0,0 @@ -#include "network.h" -#include "region_layer.h" -#include "cost_layer.h" -#include "utils.h" -#include "parser.h" -#include "box.h" -#include "demo.h" -#include "option_list.h" - -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#endif -static int coco_ids[] = {1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,27,28,31,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,70,72,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,90}; - -void train_detector(char *datacfg, char *cfgfile, char *weightfile, int *gpus, int ngpus, int clear) -{ - list *options = read_data_cfg(datacfg); - char *train_images = option_find_str(options, "train", "data/train.list"); - char *backup_directory = option_find_str(options, "backup", "/backup/"); - - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - float avg_loss = -1; - network *nets = calloc(ngpus, sizeof(network)); - - srand(time(0)); - int seed = rand(); - int i; - for(i = 0; i < ngpus; ++i){ - srand(seed); -#ifdef GPU - cuda_set_device(gpus[i]); -#endif - nets[i] = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&nets[i], weightfile); - } - if(clear) *nets[i].seen = 0; - nets[i].learning_rate *= ngpus; - } - srand(time(0)); - network net = nets[0]; - - int imgs = net.batch * net.subdivisions * ngpus; - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - data train, buffer; - - layer l = net.layers[net.n - 1]; - - int classes = l.classes; - float jitter = l.jitter; - - list *plist = get_paths(train_images); - //int N = plist->size; - char **paths = (char **)list_to_array(plist); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.paths = paths; - args.n = imgs; - args.m = plist->size; - args.classes = classes; - args.jitter = jitter; - args.num_boxes = l.max_boxes; - args.d = &buffer; - args.type = DETECTION_DATA; - args.threads = 8; - - args.angle = net.angle; - args.exposure = net.exposure; - args.saturation = net.saturation; - args.hue = net.hue; - - pthread_t load_thread = load_data(args); - clock_t time; - int count = 0; - //while(i*imgs < N*120){ - while(get_current_batch(net) < net.max_batches){ - if(l.random && count++%10 == 0){ - printf("Resizing\n"); - int dim = (rand() % 10 + 10) * 32; - if (get_current_batch(net)+200 > net.max_batches) dim = 608; - //int dim = (rand() % 4 + 16) * 32; - printf("%d\n", dim); - args.w = dim; - args.h = dim; - - pthread_join(load_thread, 0); - train = buffer; - free_data(train); - load_thread = load_data(args); - - for(i = 0; i < ngpus; ++i){ - resize_network(nets + i, dim, dim); - } - net = nets[0]; - } - time=clock(); - pthread_join(load_thread, 0); - train = buffer; - load_thread = load_data(args); - - /* - int k; - for(k = 0; k < l.max_boxes; ++k){ - box b = float_to_box(train.y.vals[10] + 1 + k*5); - if(!b.x) break; - printf("loaded: %f %f %f %f\n", b.x, b.y, b.w, b.h); - } - image im = float_to_image(448, 448, 3, train.X.vals[10]); - int k; - for(k = 0; k < l.max_boxes; ++k){ - box b = float_to_box(train.y.vals[10] + 1 + k*5); - printf("%d %d %d %d\n", truth.x, truth.y, truth.w, truth.h); - draw_bbox(im, b, 8, 1,0,0); - } - save_image(im, "truth11"); - */ - - printf("Loaded: %lf seconds\n", sec(clock()-time)); - - time=clock(); - float loss = 0; -#ifdef GPU - if(ngpus == 1){ - loss = train_network(net, train); - } else { - loss = train_networks(nets, ngpus, train, 4); - } -#else - loss = train_network(net, train); -#endif - if (avg_loss < 0) avg_loss = loss; - avg_loss = avg_loss*.9 + loss*.1; - - i = get_current_batch(net); - printf("%d: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), loss, avg_loss, get_current_rate(net), sec(clock()-time), i*imgs); - if(i%1000==0 || (i < 1000 && i%100 == 0)){ -#ifdef GPU - if(ngpus != 1) sync_nets(nets, ngpus, 0); -#endif - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory, base, i); - save_weights(net, buff); - } - free_data(train); - } -#ifdef GPU - if(ngpus != 1) sync_nets(nets, ngpus, 0); -#endif - char buff[256]; - sprintf(buff, "%s/%s_final.weights", backup_directory, base); - save_weights(net, buff); -} - - -static int get_coco_image_id(char *filename) -{ - char *p = strrchr(filename, '_'); - return atoi(p+1); -} - -static void print_cocos(FILE *fp, char *image_path, box *boxes, float **probs, int num_boxes, int classes, int w, int h) -{ - int i, j; - int image_id = get_coco_image_id(image_path); - for(i = 0; i < num_boxes; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; - - if (xmin < 0) xmin = 0; - if (ymin < 0) ymin = 0; - if (xmax > w) xmax = w; - if (ymax > h) ymax = h; - - float bx = xmin; - float by = ymin; - float bw = xmax - xmin; - float bh = ymax - ymin; - - for(j = 0; j < classes; ++j){ - if (probs[i][j]) fprintf(fp, "{\"image_id\":%d, \"category_id\":%d, \"bbox\":[%f, %f, %f, %f], \"score\":%f},\n", image_id, coco_ids[j], bx, by, bw, bh, probs[i][j]); - } - } -} - -void print_detector_detections(FILE **fps, char *id, box *boxes, float **probs, int total, int classes, int w, int h) -{ - int i, j; - for(i = 0; i < total; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; - - if (xmin < 0) xmin = 0; - if (ymin < 0) ymin = 0; - if (xmax > w) xmax = w; - if (ymax > h) ymax = h; - - for(j = 0; j < classes; ++j){ - if (probs[i][j]) fprintf(fps[j], "%s %f %f %f %f %f\n", id, probs[i][j], - xmin, ymin, xmax, ymax); - } - } -} - -void print_imagenet_detections(FILE *fp, int id, box *boxes, float **probs, int total, int classes, int w, int h) -{ - int i, j; - for(i = 0; i < total; ++i){ - float xmin = boxes[i].x - boxes[i].w/2.; - float xmax = boxes[i].x + boxes[i].w/2.; - float ymin = boxes[i].y - boxes[i].h/2.; - float ymax = boxes[i].y + boxes[i].h/2.; - - if (xmin < 0) xmin = 0; - if (ymin < 0) ymin = 0; - if (xmax > w) xmax = w; - if (ymax > h) ymax = h; - - for(j = 0; j < classes; ++j){ - int class = j; - if (probs[i][class]) fprintf(fp, "%d %d %f %f %f %f %f\n", id, j+1, probs[i][class], - xmin, ymin, xmax, ymax); - } - } -} - -void validate_detector(char *datacfg, char *cfgfile, char *weightfile, char *outfile) -{ - int j; - list *options = read_data_cfg(datacfg); - char *valid_images = option_find_str(options, "valid", "data/train.list"); - char *name_list = option_find_str(options, "names", "data/names.list"); - char *prefix = option_find_str(options, "results", "results"); - char **names = get_labels(name_list); - char *mapf = option_find_str(options, "map", 0); - int *map = 0; - if (mapf) map = read_map(mapf); - - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - srand(time(0)); - - list *plist = get_paths(valid_images); - char **paths = (char **)list_to_array(plist); - - layer l = net.layers[net.n-1]; - int classes = l.classes; - - char buff[1024]; - char *type = option_find_str(options, "eval", "voc"); - FILE *fp = 0; - FILE **fps = 0; - int coco = 0; - int imagenet = 0; - if(0==strcmp(type, "coco")){ - if(!outfile) outfile = "coco_results"; - snprintf(buff, 1024, "%s/%s.json", prefix, outfile); - fp = fopen(buff, "w"); - fprintf(fp, "[\n"); - coco = 1; - } else if(0==strcmp(type, "imagenet")){ - if(!outfile) outfile = "imagenet-detection"; - snprintf(buff, 1024, "%s/%s.txt", prefix, outfile); - fp = fopen(buff, "w"); - imagenet = 1; - classes = 200; - } else { - if(!outfile) outfile = "comp4_det_test_"; - fps = calloc(classes, sizeof(FILE *)); - for(j = 0; j < classes; ++j){ - snprintf(buff, 1024, "%s/%s%s.txt", prefix, outfile, names[j]); - fps[j] = fopen(buff, "w"); - } - } - - - box *boxes = calloc(l.w*l.h*l.n, sizeof(box)); - float **probs = calloc(l.w*l.h*l.n, sizeof(float *)); - for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); - - int m = plist->size; - int i=0; - int t; - - float thresh = .005; - float nms = .45; - - int nthreads = 4; - image *val = calloc(nthreads, sizeof(image)); - image *val_resized = calloc(nthreads, sizeof(image)); - image *buf = calloc(nthreads, sizeof(image)); - image *buf_resized = calloc(nthreads, sizeof(image)); - pthread_t *thr = calloc(nthreads, sizeof(pthread_t)); - - load_args args = {0}; - args.w = net.w; - args.h = net.h; - args.type = IMAGE_DATA; - - for(t = 0; t < nthreads; ++t){ - args.path = paths[i+t]; - args.im = &buf[t]; - args.resized = &buf_resized[t]; - thr[t] = load_data_in_thread(args); - } - time_t start = time(0); - for(i = nthreads; i < m+nthreads; i += nthreads){ - fprintf(stderr, "%d\n", i); - for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ - pthread_join(thr[t], 0); - val[t] = buf[t]; - val_resized[t] = buf_resized[t]; - } - for(t = 0; t < nthreads && i+t < m; ++t){ - args.path = paths[i+t]; - args.im = &buf[t]; - args.resized = &buf_resized[t]; - thr[t] = load_data_in_thread(args); - } - for(t = 0; t < nthreads && i+t-nthreads < m; ++t){ - char *path = paths[i+t-nthreads]; - char *id = basecfg(path); - float *X = val_resized[t].data; - network_predict(net, X); - int w = val[t].w; - int h = val[t].h; - get_region_boxes(l, w, h, thresh, probs, boxes, 0, map, .5); - if (nms) do_nms_sort(boxes, probs, l.w*l.h*l.n, classes, nms); - if (coco){ - print_cocos(fp, path, boxes, probs, l.w*l.h*l.n, classes, w, h); - } else if (imagenet){ - print_imagenet_detections(fp, i+t-nthreads+1, boxes, probs, l.w*l.h*l.n, classes, w, h); - } else { - print_detector_detections(fps, id, boxes, probs, l.w*l.h*l.n, classes, w, h); - } - free(id); - free_image(val[t]); - free_image(val_resized[t]); - } - } - for(j = 0; j < classes; ++j){ - if(fps) fclose(fps[j]); - } - if(coco){ - fseek(fp, -2, SEEK_CUR); - fprintf(fp, "\n]\n"); - fclose(fp); - } - fprintf(stderr, "Total Detection Time: %f Seconds\n", (double)(time(0) - start)); -} - -void validate_detector_recall(char *cfgfile, char *weightfile) -{ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - fprintf(stderr, "Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - srand(time(0)); - - list *plist = get_paths("data/voc.2007.test"); - char **paths = (char **)list_to_array(plist); - - layer l = net.layers[net.n-1]; - int classes = l.classes; - - int j, k; - box *boxes = calloc(l.w*l.h*l.n, sizeof(box)); - float **probs = calloc(l.w*l.h*l.n, sizeof(float *)); - for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = calloc(classes, sizeof(float *)); - - int m = plist->size; - int i=0; - - float thresh = .001; - float iou_thresh = .5; - float nms = .4; - - int total = 0; - int correct = 0; - int proposals = 0; - float avg_iou = 0; - - for(i = 0; i < m; ++i){ - char *path = paths[i]; - image orig = load_image_color(path, 0, 0); - image sized = resize_image(orig, net.w, net.h); - char *id = basecfg(path); - network_predict(net, sized.data); - get_region_boxes(l, 1, 1, thresh, probs, boxes, 1, 0, .5); - if (nms) do_nms(boxes, probs, l.w*l.h*l.n, 1, nms); - - char labelpath[4096]; - find_replace(path, "images", "labels", labelpath); - find_replace(labelpath, "JPEGImages", "labels", labelpath); - find_replace(labelpath, ".jpg", ".txt", labelpath); - find_replace(labelpath, ".JPEG", ".txt", labelpath); - - int num_labels = 0; - box_label *truth = read_boxes(labelpath, &num_labels); - for(k = 0; k < l.w*l.h*l.n; ++k){ - if(probs[k][0] > thresh){ - ++proposals; - } - } - for (j = 0; j < num_labels; ++j) { - ++total; - box t = {truth[j].x, truth[j].y, truth[j].w, truth[j].h}; - float best_iou = 0; - for(k = 0; k < l.w*l.h*l.n; ++k){ - float iou = box_iou(boxes[k], t); - if(probs[k][0] > thresh && iou > best_iou){ - best_iou = iou; - } - } - avg_iou += best_iou; - if(best_iou > iou_thresh){ - ++correct; - } - } - - fprintf(stderr, "%5d %5d %5d\tRPs/Img: %.2f\tIOU: %.2f%%\tRecall:%.2f%%\n", i, correct, total, (float)proposals/(i+1), avg_iou*100/total, 100.*correct/total); - free(id); - free_image(orig); - free_image(sized); - } -} - -void test_detector(char *datacfg, char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh) -{ - list *options = read_data_cfg(datacfg); - char *name_list = option_find_str(options, "names", "data/names.list"); - char **names = get_labels(name_list); - - image **alphabet = load_alphabet(); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - srand(2222222); - clock_t time; - char buff[256]; - char *input = buff; - int j; - float nms=.4; - 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, "\n"); - } - image im = load_image_color(input,0,0); - image sized = resize_image(im, net.w, net.h); - layer l = net.layers[net.n-1]; - - box *boxes = calloc(l.w*l.h*l.n, sizeof(box)); - float **probs = calloc(l.w*l.h*l.n, sizeof(float *)); - for(j = 0; j < l.w*l.h*l.n; ++j) probs[j] = calloc(l.classes + 1, sizeof(float *)); - - float *X = sized.data; - time=clock(); - network_predict(net, X); - printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - get_region_boxes(l, 1, 1, thresh, probs, boxes, 0, 0, hier_thresh); - if (l.softmax_tree && nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms); - else if (nms) do_nms_sort(boxes, probs, l.w*l.h*l.n, l.classes, nms); - draw_detections(im, l.w*l.h*l.n, thresh, boxes, probs, names, alphabet, l.classes); - save_image(im, "predictions"); - show_image(im, "predictions"); - - free_image(im); - free_image(sized); - free(boxes); - free_ptrs((void **)probs, l.w*l.h*l.n); -#ifdef OPENCV - cvWaitKey(0); - cvDestroyAllWindows(); -#endif - if (filename) break; - } -} - -void run_detector(int argc, char **argv) -{ - char *prefix = find_char_arg(argc, argv, "-prefix", 0); - float thresh = find_float_arg(argc, argv, "-thresh", .24); - float hier_thresh = find_float_arg(argc, argv, "-hier", .5); - int cam_index = find_int_arg(argc, argv, "-c", 0); - int frame_skip = find_int_arg(argc, argv, "-s", 0); - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - char *gpu_list = find_char_arg(argc, argv, "-gpus", 0); - char *outfile = find_char_arg(argc, argv, "-out", 0); - int *gpus = 0; - int gpu = 0; - int ngpus = 0; - if(gpu_list){ - printf("%s\n", gpu_list); - int len = strlen(gpu_list); - ngpus = 1; - int i; - for(i = 0; i < len; ++i){ - if (gpu_list[i] == ',') ++ngpus; - } - gpus = calloc(ngpus, sizeof(int)); - for(i = 0; i < ngpus; ++i){ - gpus[i] = atoi(gpu_list); - gpu_list = strchr(gpu_list, ',')+1; - } - } else { - gpu = gpu_index; - gpus = &gpu; - ngpus = 1; - } - - int clear = find_arg(argc, argv, "-clear"); - - char *datacfg = argv[3]; - char *cfg = argv[4]; - char *weights = (argc > 5) ? argv[5] : 0; - char *filename = (argc > 6) ? argv[6]: 0; - if(0==strcmp(argv[2], "test")) test_detector(datacfg, cfg, weights, filename, thresh, hier_thresh); - else if(0==strcmp(argv[2], "train")) train_detector(datacfg, cfg, weights, gpus, ngpus, clear); - else if(0==strcmp(argv[2], "valid")) validate_detector(datacfg, cfg, weights, outfile); - else if(0==strcmp(argv[2], "recall")) validate_detector_recall(cfg, weights); - else if(0==strcmp(argv[2], "demo")) { - list *options = read_data_cfg(datacfg); - int classes = option_find_int(options, "classes", 20); - char *name_list = option_find_str(options, "names", "data/names.list"); - char **names = get_labels(name_list); - demo(cfg, weights, thresh, cam_index, filename, names, classes, frame_skip, prefix, hier_thresh); - } -} diff --git a/image.darknet/inst/include/darknet/src/dropout_layer.c b/image.darknet/inst/include/darknet/src/dropout_layer.c index b1381e6..780554f 100644 --- a/image.darknet/inst/include/darknet/src/dropout_layer.c +++ b/image.darknet/inst/include/darknet/src/dropout_layer.c @@ -35,26 +35,26 @@ void resize_dropout_layer(dropout_layer *l, int inputs) #endif } -void forward_dropout_layer(dropout_layer l, network_state state) +void forward_dropout_layer(dropout_layer l, network net) { int i; - if (!state.train) return; + if (!net.train) return; for(i = 0; i < l.batch * l.inputs; ++i){ float r = rand_uniform(0, 1); l.rand[i] = r; - if(r < l.probability) state.input[i] = 0; - else state.input[i] *= l.scale; + if(r < l.probability) net.input[i] = 0; + else net.input[i] *= l.scale; } } -void backward_dropout_layer(dropout_layer l, network_state state) +void backward_dropout_layer(dropout_layer l, network net) { int i; - if(!state.delta) return; + if(!net.delta) return; for(i = 0; i < l.batch * l.inputs; ++i){ float r = l.rand[i]; - if(r < l.probability) state.delta[i] = 0; - else state.delta[i] *= l.scale; + if(r < l.probability) net.delta[i] = 0; + else net.delta[i] *= l.scale; } } diff --git a/image.darknet/inst/include/darknet/src/dropout_layer.h b/image.darknet/inst/include/darknet/src/dropout_layer.h index 691cfc5..01f94d4 100644 --- a/image.darknet/inst/include/darknet/src/dropout_layer.h +++ b/image.darknet/inst/include/darknet/src/dropout_layer.h @@ -8,13 +8,13 @@ typedef layer dropout_layer; dropout_layer make_dropout_layer(int batch, int inputs, float probability); -void forward_dropout_layer(dropout_layer l, network_state state); -void backward_dropout_layer(dropout_layer l, network_state state); +void forward_dropout_layer(dropout_layer l, network net); +void backward_dropout_layer(dropout_layer l, network net); void resize_dropout_layer(dropout_layer *l, int inputs); #ifdef GPU -void forward_dropout_layer_gpu(dropout_layer l, network_state state); -void backward_dropout_layer_gpu(dropout_layer l, network_state state); +void forward_dropout_layer_gpu(dropout_layer l, network net); +void backward_dropout_layer_gpu(dropout_layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/dropout_layer_kernels.cu b/image.darknet/inst/include/darknet/src/dropout_layer_kernels.cu index 7e51bd5..bd12b67 100644 --- a/image.darknet/inst/include/darknet/src/dropout_layer_kernels.cu +++ b/image.darknet/inst/include/darknet/src/dropout_layer_kernels.cu @@ -14,9 +14,9 @@ __global__ void yoloswag420blazeit360noscope(float *input, int size, float *rand if(id < size) input[id] = (rand[id] < prob) ? 0 : input[id]*scale; } -void forward_dropout_layer_gpu(dropout_layer layer, network_state state) +void forward_dropout_layer_gpu(dropout_layer layer, network net) { - if (!state.train) return; + if (!net.train) return; int size = layer.inputs*layer.batch; cuda_random(layer.rand_gpu, size); /* @@ -27,15 +27,15 @@ void forward_dropout_layer_gpu(dropout_layer layer, network_state state) cuda_push_array(layer.rand_gpu, layer.rand, size); */ - yoloswag420blazeit360noscope<<>>(state.input, size, layer.rand_gpu, layer.probability, layer.scale); + yoloswag420blazeit360noscope<<>>(net.input_gpu, size, layer.rand_gpu, layer.probability, layer.scale); check_error(cudaPeekAtLastError()); } -void backward_dropout_layer_gpu(dropout_layer layer, network_state state) +void backward_dropout_layer_gpu(dropout_layer layer, network net) { - if(!state.delta) return; + if(!net.delta_gpu) return; int size = layer.inputs*layer.batch; - yoloswag420blazeit360noscope<<>>(state.delta, size, layer.rand_gpu, layer.probability, layer.scale); + yoloswag420blazeit360noscope<<>>(net.delta_gpu, size, layer.rand_gpu, layer.probability, layer.scale); check_error(cudaPeekAtLastError()); } diff --git a/image.darknet/inst/include/darknet/src/gemm.c b/image.darknet/inst/include/darknet/src/gemm.c index 3003be0..648027f 100644 --- a/image.darknet/inst/include/darknet/src/gemm.c +++ b/image.darknet/inst/include/darknet/src/gemm.c @@ -77,6 +77,7 @@ void gemm_nn(int M, int N, int K, float ALPHA, float *C, int ldc) { int i,j,k; + #pragma omp parallel for for(i = 0; i < M; ++i){ for(k = 0; k < K; ++k){ register float A_PART = ALPHA*A[i*lda+k]; @@ -93,6 +94,7 @@ void gemm_nt(int M, int N, int K, float ALPHA, float *C, int ldc) { int i,j,k; + #pragma omp parallel for for(i = 0; i < M; ++i){ for(j = 0; j < N; ++j){ register float sum = 0; @@ -110,6 +112,7 @@ void gemm_tn(int M, int N, int K, float ALPHA, float *C, int ldc) { int i,j,k; + #pragma omp parallel for for(i = 0; i < M; ++i){ for(k = 0; k < K; ++k){ register float A_PART = ALPHA*A[k*lda+i]; @@ -126,6 +129,7 @@ void gemm_tt(int M, int N, int K, float ALPHA, float *C, int ldc) { int i,j,k; + #pragma omp parallel for for(i = 0; i < M; ++i){ for(j = 0; j < N; ++j){ register float sum = 0; @@ -165,7 +169,7 @@ void gemm_cpu(int TA, int TB, int M, int N, int K, float ALPHA, #include -void gemm_ongpu(int TA, int TB, int M, int N, int K, float ALPHA, +void gemm_gpu(int TA, int TB, int M, int N, int K, float ALPHA, float *A_gpu, int lda, float *B_gpu, int ldb, float BETA, @@ -177,24 +181,6 @@ void gemm_ongpu(int TA, int TB, int M, int N, int K, float ALPHA, check_error(status); } -void gemm_gpu(int TA, int TB, int M, int N, int K, float ALPHA, - float *A, int lda, - float *B, int ldb, - float BETA, - float *C, int ldc) -{ - float *A_gpu = cuda_make_array(A, (TA ? lda*K:lda*M)); - float *B_gpu = cuda_make_array(B, (TB ? ldb*N : ldb*K)); - float *C_gpu = cuda_make_array(C, ldc*M); - - gemm_ongpu(TA, TB, M, N, K, ALPHA, A_gpu, lda, B_gpu, ldb, BETA, C_gpu, ldc); - - cuda_pull_array(C_gpu, C, ldc*M); - cuda_free(A_gpu); - cuda_free(B_gpu); - cuda_free(C_gpu); -} - #include #include #include @@ -224,7 +210,7 @@ void time_gpu_random_matrix(int TA, int TB, int m, int k, int n) free(c); } -void time_ongpu(int TA, int TB, int m, int k, int n) +void time_gpu(int TA, int TB, int m, int k, int n) { int iter = 10; float *a = random_matrix(m,k); @@ -242,7 +228,7 @@ void time_ongpu(int TA, int TB, int m, int k, int n) int i; clock_t start = clock(), end; for(i = 0; i= m.n){ - m.n *= 2; - m.data = realloc(m.data, m.n*sizeof(char*)); - } - m.data[count] = line; - ++count; - } - printf("%d\n", count); - m.n = count; - m.data = realloc(m.data, count*sizeof(char*)); - return m; -} - -void string_to_board(char *s, float *board) -{ - int i, j; - //memset(board, 0, 1*19*19*sizeof(float)); - int count = 0; - for(i = 0; i < 91; ++i){ - char c = s[i]; - for(j = 0; j < 4; ++j){ - int me = (c >> (2*j)) & 1; - int you = (c >> (2*j + 1)) & 1; - if (me) board[count] = 1; - else if (you) board[count] = -1; - else board[count] = 0; - ++count; - if(count >= 19*19) break; - } - } -} - -void board_to_string(char *s, float *board) -{ - int i, j; - memset(s, 0, (19*19/4+1)*sizeof(char)); - int count = 0; - for(i = 0; i < 91; ++i){ - for(j = 0; j < 4; ++j){ - int me = (board[count] == 1); - int you = (board[count] == -1); - if (me) s[i] = s[i] | (1<<(2*j)); - if (you) s[i] = s[i] | (1<<(2*j + 1)); - ++count; - if(count >= 19*19) break; - } - } -} - -void random_go_moves(moves m, float *boards, float *labels, int n) -{ - int i; - memset(labels, 0, 19*19*n*sizeof(float)); - for(i = 0; i < n; ++i){ - char *b = m.data[rand()%m.n]; - int row = b[0]; - int col = b[1]; - labels[col + 19*(row + i*19)] = 1; - string_to_board(b+2, boards+i*19*19); - boards[col + 19*(row + i*19)] = 0; - - int flip = rand()%2; - int rotate = rand()%4; - image in = float_to_image(19, 19, 1, boards+i*19*19); - image out = float_to_image(19, 19, 1, labels+i*19*19); - if(flip){ - flip_image(in); - flip_image(out); - } - rotate_image_cw(in, rotate); - rotate_image_cw(out, rotate); - } -} - - -void train_go(char *cfgfile, char *weightfile) -{ - srand(time(0)); - float avg_loss = -1; - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - - char *backup_directory = "/home/pjreddie/backup/"; - - char buff[256]; - float *board = calloc(19*19*net.batch, sizeof(float)); - float *move = calloc(19*19*net.batch, sizeof(float)); - moves m = load_go_moves("/home/pjreddie/backup/go.train"); - //moves m = load_go_moves("games.txt"); - - int N = m.n; - int epoch = (*net.seen)/N; - while(get_current_batch(net) < net.max_batches || net.max_batches == 0){ - clock_t time=clock(); - - random_go_moves(m, board, move, net.batch); - float loss = train_network_datum(net, board, move) / net.batch; - if(avg_loss == -1) avg_loss = loss; - avg_loss = avg_loss*.95 + loss*.05; - printf("%d, %.3f: %f, %f avg, %f rate, %lf seconds, %d images\n", get_current_batch(net), (float)(*net.seen)/N, loss, avg_loss, get_current_rate(net), sec(clock()-time), *net.seen); - if(*net.seen/N > epoch){ - epoch = *net.seen/N; - char buff[256]; - sprintf(buff, "%s/%s_%d.weights", backup_directory,base, epoch); - save_weights(net, buff); - - } - if(get_current_batch(net)%100 == 0){ - char buff[256]; - sprintf(buff, "%s/%s.backup",backup_directory,base); - save_weights(net, buff); - } - if(get_current_batch(net)%10000 == 0){ - char buff[256]; - sprintf(buff, "%s/%s_%d.backup",backup_directory,base,get_current_batch(net)); - save_weights(net, buff); - } - } - sprintf(buff, "%s/%s.weights", backup_directory, base); - save_weights(net, buff); - - free_network(net); - free(base); -} - -void propagate_liberty(float *board, int *lib, int *visited, int row, int col, int side) -{ - if (row < 0 || row > 18 || col < 0 || col > 18) return; - int index = row*19 + col; - if (board[index] != side) return; - if (visited[index]) return; - visited[index] = 1; - lib[index] += 1; - propagate_liberty(board, lib, visited, row+1, col, side); - propagate_liberty(board, lib, visited, row-1, col, side); - propagate_liberty(board, lib, visited, row, col+1, side); - propagate_liberty(board, lib, visited, row, col-1, side); -} - - -int *calculate_liberties(float *board) -{ - int *lib = calloc(19*19, sizeof(int)); - int visited[361]; - int i, j; - for(j = 0; j < 19; ++j){ - for(i = 0; i < 19; ++i){ - memset(visited, 0, 19*19*sizeof(int)); - int index = j*19 + i; - if(board[index] == 0){ - if ((i > 0) && board[index - 1]) propagate_liberty(board, lib, visited, j, i-1, board[index-1]); - if ((i < 18) && board[index + 1]) propagate_liberty(board, lib, visited, j, i+1, board[index+1]); - if ((j > 0) && board[index - 19]) propagate_liberty(board, lib, visited, j-1, i, board[index-19]); - if ((j < 18) && board[index + 19]) propagate_liberty(board, lib, visited, j+1, i, board[index+19]); - } - } - } - return lib; -} - -void print_board(float *board, int swap, int *indexes) -{ - //FILE *stream = stdout; - FILE *stream = stderr; - int i,j,n; - fprintf(stream, "\n\n"); - fprintf(stream, " "); - for(i = 0; i < 19; ++i){ - fprintf(stream, "%c ", 'A' + i + 1*(i > 7 && noi)); - } - fprintf(stream, "\n"); - for(j = 0; j < 19; ++j){ - fprintf(stream, "%2d", (inverted) ? 19-j : j+1); - for(i = 0; i < 19; ++i){ - int index = j*19 + i; - if(indexes){ - int found = 0; - for(n = 0; n < nind; ++n){ - if(index == indexes[n]){ - found = 1; - /* - if(n == 0) fprintf(stream, "\uff11"); - else if(n == 1) fprintf(stream, "\uff12"); - else if(n == 2) fprintf(stream, "\uff13"); - else if(n == 3) fprintf(stream, "\uff14"); - else if(n == 4) fprintf(stream, "\uff15"); - */ - if(n == 0) fprintf(stream, " 1"); - else if(n == 1) fprintf(stream, " 2"); - else if(n == 2) fprintf(stream, " 3"); - else if(n == 3) fprintf(stream, " 4"); - else if(n == 4) fprintf(stream, " 5"); - } - } - if(found) continue; - } - //if(board[index]*-swap > 0) fprintf(stream, "\u25C9 "); - //else if(board[index]*-swap < 0) fprintf(stream, "\u25EF "); - if(board[index]*-swap > 0) fprintf(stream, " O"); - else if(board[index]*-swap < 0) fprintf(stream, " X"); - else fprintf(stream, " "); - } - fprintf(stream, "\n"); - } -} - -void flip_board(float *board) -{ - int i; - for(i = 0; i < 19*19; ++i){ - board[i] = -board[i]; - } -} - -void predict_move(network net, float *board, float *move, int multi) -{ - float *output = network_predict(net, board); - copy_cpu(19*19, output, 1, move, 1); - int i; - if(multi){ - image bim = float_to_image(19, 19, 1, board); - for(i = 1; i < 8; ++i){ - rotate_image_cw(bim, i); - if(i >= 4) flip_image(bim); - - float *output = network_predict(net, board); - image oim = float_to_image(19, 19, 1, output); - - if(i >= 4) flip_image(oim); - rotate_image_cw(oim, -i); - - axpy_cpu(19*19, 1, output, 1, move, 1); - - if(i >= 4) flip_image(bim); - rotate_image_cw(bim, -i); - } - scal_cpu(19*19, 1./8., move, 1); - } - for(i = 0; i < 19*19; ++i){ - if(board[i]) move[i] = 0; - } -} - -void remove_connected(float *b, int *lib, int p, int r, int c) -{ - if (r < 0 || r >= 19 || c < 0 || c >= 19) return; - if (b[r*19 + c] != p) return; - if (lib[r*19 + c] != 1) return; - b[r*19 + c] = 0; - remove_connected(b, lib, p, r+1, c); - remove_connected(b, lib, p, r-1, c); - remove_connected(b, lib, p, r, c+1); - remove_connected(b, lib, p, r, c-1); -} - - -void move_go(float *b, int p, int r, int c) -{ - int *l = calculate_liberties(b); - b[r*19 + c] = p; - remove_connected(b, l, -p, r+1, c); - remove_connected(b, l, -p, r-1, c); - remove_connected(b, l, -p, r, c+1); - remove_connected(b, l, -p, r, c-1); - free(l); -} - -int makes_safe_go(float *b, int *lib, int p, int r, int c){ - if (r < 0 || r >= 19 || c < 0 || c >= 19) return 0; - if (b[r*19 + c] == -p){ - if (lib[r*19 + c] > 1) return 0; - else return 1; - } - if (b[r*19 + c] == 0) return 1; - if (lib[r*19 + c] > 1) return 1; - return 0; -} - -int suicide_go(float *b, int p, int r, int c) -{ - int *l = calculate_liberties(b); - int safe = 0; - safe = safe || makes_safe_go(b, l, p, r+1, c); - safe = safe || makes_safe_go(b, l, p, r-1, c); - safe = safe || makes_safe_go(b, l, p, r, c+1); - safe = safe || makes_safe_go(b, l, p, r, c-1); - free(l); - return !safe; -} - -int legal_go(float *b, char *ko, int p, int r, int c) -{ - if (b[r*19 + c]) return 0; - char curr[91]; - char next[91]; - board_to_string(curr, b); - move_go(b, p, r, c); - board_to_string(next, b); - string_to_board(curr, b); - if(memcmp(next, ko, 91) == 0) return 0; - return 1; -} - -int generate_move(network net, int player, float *board, int multi, float thresh, float temp, char *ko, int print) -{ - int i, j; - for(i = 0; i < net.n; ++i) net.layers[i].temperature = temp; - - float move[361]; - if (player < 0) flip_board(board); - predict_move(net, board, move, multi); - if (player < 0) flip_board(board); - - - for(i = 0; i < 19; ++i){ - for(j = 0; j < 19; ++j){ - if (!legal_go(board, ko, player, i, j)) move[i*19 + j] = 0; - } - } - - int indexes[nind]; - top_k(move, 19*19, nind, indexes); - if(thresh > move[indexes[0]]) thresh = move[indexes[nind-1]]; - - for(i = 0; i < 19; ++i){ - for(j = 0; j < 19; ++j){ - if (move[i*19 + j] < thresh) move[i*19 + j] = 0; - } - } - - - int max = max_index(move, 19*19); - int row = max / 19; - int col = max % 19; - int index = sample_array(move, 19*19); - - if(print){ - top_k(move, 19*19, nind, indexes); - for(i = 0; i < nind; ++i){ - if (!move[indexes[i]]) indexes[i] = -1; - } - print_board(board, player, indexes); - for(i = 0; i < nind; ++i){ - fprintf(stderr, "%d: %f\n", i+1, move[indexes[i]]); - } - } - - if(suicide_go(board, player, row, col)){ - return -1; - } - if(suicide_go(board, player, index/19, index%19)) index = max; - return index; -} - -void valid_go(char *cfgfile, char *weightfile, int multi) -{ - srand(time(0)); - char *base = basecfg(cfgfile); - printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); - printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); - - float *board = calloc(19*19, sizeof(float)); - float *move = calloc(19*19, sizeof(float)); - moves m = load_go_moves("/home/pjreddie/backup/go.test"); - - int N = m.n; - int i; - int correct = 0; - for(i = 0; i = 'A' && c <= 'Z') c = c - 'A'; - if(c >= 'a' && c <= 'z') c = c - 'a'; - if(c >= 8) --c; - r = 19 - r; - fprintf(stderr, "move: %d %d\n", r, c); - - char *swap = two; - two = one; - one = swap; - move_go(board, player, r, c); - board_to_string(one, board); - - printf("=%s \n\n", ids); - print_board(board, 1, 0); - } else if (!strcmp(buff, "genmove")){ - char color[256]; - scanf("%s", color); - int player = (color[0] == 'b' || color[0] == 'B') ? 1 : -1; - - int index = generate_move(net, player, board, multi, .1, .7, two, 1); - if(passed || index < 0){ - printf("=%s pass\n\n", ids); - passed = 0; - } else { - int row = index / 19; - int col = index % 19; - - char *swap = two; - two = one; - one = swap; - - move_go(board, player, row, col); - board_to_string(one, board); - row = 19 - row; - if (col >= 8) ++col; - printf("=%s %c%d\n\n", ids, 'A' + col, row); - print_board(board, 1, 0); - } - - } else if (!strcmp(buff, "p")){ - //print_board(board, 1, 0); - } else if (!strcmp(buff, "final_status_list")){ - char type[256]; - scanf("%s", type); - fprintf(stderr, "final_status\n"); - char *line = fgetl(stdin); - free(line); - if(type[0] == 'd' || type[0] == 'D'){ - FILE *f = fopen("game.txt", "w"); - int i, j; - int count = 2; - fprintf(f, "boardsize 19\n"); - fprintf(f, "clear_board\n"); - for(j = 0; j < 19; ++j){ - for(i = 0; i < 19; ++i){ - if(board[j*19 + i] == 1) fprintf(f, "play black %c%d\n", 'A'+i+(i>=8), 19-j); - if(board[j*19 + i] == -1) fprintf(f, "play white %c%d\n", 'A'+i+(i>=8), 19-j); - if(board[j*19 + i]) ++count; - } - } - fprintf(f, "final_status_list dead\n"); - fclose(f); - FILE *p = popen("./gnugo --mode gtp < game.txt", "r"); - for(i = 0; i < count; ++i){ - free(fgetl(p)); - free(fgetl(p)); - } - char *l = 0; - while((l = fgetl(p))){ - printf("%s\n", l); - free(l); - } - } else { - printf("?%s unknown command\n\n", ids); - } - } else { - char *line = fgetl(stdin); - free(line); - printf("?%s unknown command\n\n", ids); - } - fflush(stdout); - fflush(stderr); - } -} - -void test_go(char *cfg, char *weights, int multi) -{ - network net = parse_network_cfg(cfg); - if(weights){ - load_weights(&net, weights); - } - srand(time(0)); - set_batch_network(&net, 1); - float *board = calloc(19*19, sizeof(float)); - float *move = calloc(19*19, sizeof(float)); - int color = 1; - while(1){ - float *output = network_predict(net, board); - copy_cpu(19*19, output, 1, move, 1); - int i; - if(multi){ - image bim = float_to_image(19, 19, 1, board); - for(i = 1; i < 8; ++i){ - rotate_image_cw(bim, i); - if(i >= 4) flip_image(bim); - - float *output = network_predict(net, board); - image oim = float_to_image(19, 19, 1, output); - - if(i >= 4) flip_image(oim); - rotate_image_cw(oim, -i); - - axpy_cpu(19*19, 1, output, 1, move, 1); - - if(i >= 4) flip_image(bim); - rotate_image_cw(bim, -i); - } - scal_cpu(19*19, 1./8., move, 1); - } - for(i = 0; i < 19*19; ++i){ - if(board[i]) move[i] = 0; - } - - int indexes[nind]; - int row, col; - top_k(move, 19*19, nind, indexes); - print_board(board, color, indexes); - for(i = 0; i < nind; ++i){ - int index = indexes[i]; - row = index / 19; - col = index % 19; - printf("%d: %c %d, %.2f%%\n", i+1, col + 'A' + 1*(col > 7 && noi), (inverted)?19 - row : row+1, move[index]*100); - } - //if(color == 1) printf("\u25EF Enter move: "); - //else printf("\u25C9 Enter move: "); - if(color == 1) printf("X Enter move: "); - else printf("O Enter move: "); - - char c; - char *line = fgetl(stdin); - int picked = 1; - int dnum = sscanf(line, "%d", &picked); - int cnum = sscanf(line, "%c", &c); - if (strlen(line) == 0 || dnum) { - --picked; - if (picked < nind){ - int index = indexes[picked]; - row = index / 19; - col = index % 19; - board[row*19 + col] = 1; - } - } else if (cnum){ - if (c <= 'T' && c >= 'A'){ - int num = sscanf(line, "%c %d", &c, &row); - row = (inverted)?19 - row : row-1; - col = c - 'A'; - if (col > 7 && noi) col -= 1; - if (num == 2) board[row*19 + col] = 1; - } else if (c == 'p') { - // Pass - } else if(c=='b' || c == 'w'){ - char g; - int num = sscanf(line, "%c %c %d", &g, &c, &row); - row = (inverted)?19 - row : row-1; - col = c - 'A'; - if (col > 7 && noi) col -= 1; - if (num == 3) board[row*19 + col] = (g == 'b') ? color : -color; - } else if(c == 'c'){ - char g; - int num = sscanf(line, "%c %c %d", &g, &c, &row); - row = (inverted)?19 - row : row-1; - col = c - 'A'; - if (col > 7 && noi) col -= 1; - if (num == 3) board[row*19 + col] = 0; - } - } - free(line); - flip_board(board); - color = -color; - } -} - -float score_game(float *board) -{ - FILE *f = fopen("game.txt", "w"); - int i, j; - int count = 3; - fprintf(f, "komi 6.5\n"); - fprintf(f, "boardsize 19\n"); - fprintf(f, "clear_board\n"); - for(j = 0; j < 19; ++j){ - for(i = 0; i < 19; ++i){ - if(board[j*19 + i] == 1) fprintf(f, "play black %c%d\n", 'A'+i+(i>=8), 19-j); - if(board[j*19 + i] == -1) fprintf(f, "play white %c%d\n", 'A'+i+(i>=8), 19-j); - if(board[j*19 + i]) ++count; - } - } - fprintf(f, "final_score\n"); - fclose(f); - FILE *p = popen("./gnugo --mode gtp < game.txt", "r"); - for(i = 0; i < count; ++i){ - free(fgetl(p)); - free(fgetl(p)); - } - char *l = 0; - float score = 0; - char player = 0; - while((l = fgetl(p))){ - fprintf(stderr, "%s \t", l); - int n = sscanf(l, "= %c+%f", &player, &score); - free(l); - if (n == 2) break; - } - if(player == 'W') score = -score; - pclose(p); - return score; -} - -void self_go(char *filename, char *weightfile, char *f2, char *w2, int multi) -{ - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } - - network net2 = net; - if(f2){ - net2 = parse_network_cfg(f2); - if(w2){ - load_weights(&net2, w2); - } - } - srand(time(0)); - char boards[300][93]; - int count = 0; - set_batch_network(&net, 1); - set_batch_network(&net2, 1); - float *board = calloc(19*19, sizeof(float)); - char *one = calloc(91, sizeof(char)); - char *two = calloc(91, sizeof(char)); - int done = 0; - int player = 1; - int p1 = 0; - int p2 = 0; - int total = 0; - while(1){ - if (done || count >= 300){ - float score = score_game(board); - int i = (score > 0)? 0 : 1; - if((score > 0) == (total%2==0)) ++p1; - else ++p2; - ++total; - fprintf(stderr, "Total: %d, Player 1: %f, Player 2: %f\n", total, (float)p1/total, (float)p2/total); - int j; - for(; i < count; i += 2){ - for(j = 0; j < 93; ++j){ - printf("%c", boards[i][j]); - } - printf("\n"); - } - memset(board, 0, 19*19*sizeof(float)); - player = 1; - done = 0; - count = 0; - fflush(stdout); - fflush(stderr); - } - //print_board(board, 1, 0); - //sleep(1); - network use = ((total%2==0) == (player==1)) ? net : net2; - int index = generate_move(use, player, board, multi, .1, .7, two, 0); - if(index < 0){ - done = 1; - continue; - } - int row = index / 19; - int col = index % 19; - - char *swap = two; - two = one; - one = swap; - - if(player < 0) flip_board(board); - boards[count][0] = row; - boards[count][1] = col; - board_to_string(boards[count] + 2, board); - if(player < 0) flip_board(board); - ++count; - - move_go(board, player, row, col); - board_to_string(one, board); - - player = -player; - } -} - -void run_go(int argc, char **argv) -{ - //boards_go(); - if(argc < 4){ - fprintf(stderr, "usage: %s %s [train/test/valid] [cfg] [weights (optional)]\n", argv[0], argv[1]); - return; - } - - char *cfg = argv[3]; - char *weights = (argc > 4) ? argv[4] : 0; - char *c2 = (argc > 5) ? argv[5] : 0; - char *w2 = (argc > 6) ? argv[6] : 0; - int multi = find_arg(argc, argv, "-multi"); - if(0==strcmp(argv[2], "train")) train_go(cfg, weights); - else if(0==strcmp(argv[2], "valid")) valid_go(cfg, weights, multi); - else if(0==strcmp(argv[2], "self")) self_go(cfg, weights, c2, w2, multi); - else if(0==strcmp(argv[2], "test")) test_go(cfg, weights, multi); - else if(0==strcmp(argv[2], "engine")) engine_go(cfg, weights, multi); -} - - diff --git a/image.darknet/inst/include/darknet/src/gru_layer.c b/image.darknet/inst/include/darknet/src/gru_layer.c index b78e868..b6601d8 100644 --- a/image.darknet/inst/include/darknet/src/gru_layer.c +++ b/image.darknet/inst/include/darknet/src/gru_layer.c @@ -26,7 +26,7 @@ static void increment_layer(layer *l, int steps) #endif } -layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_normalize) +layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_normalize, int adam) { fprintf(stderr, "GRU Layer: %d inputs, %d outputs\n", inputs, outputs); batch = batch / steps; @@ -36,39 +36,37 @@ layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_no l.steps = steps; l.inputs = inputs; - l.input_z_layer = malloc(sizeof(layer)); + l.uz = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_z_layer) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize); - l.input_z_layer->batch = batch; + *(l.uz) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.uz->batch = batch; - l.state_z_layer = malloc(sizeof(layer)); + l.wz = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.state_z_layer) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize); - l.state_z_layer->batch = batch; + *(l.wz) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wz->batch = batch; - - - l.input_r_layer = malloc(sizeof(layer)); + l.ur = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_r_layer) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize); - l.input_r_layer->batch = batch; + *(l.ur) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.ur->batch = batch; - l.state_r_layer = malloc(sizeof(layer)); + l.wr = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.state_r_layer) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize); - l.state_r_layer->batch = batch; + *(l.wr) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wr->batch = batch; - l.input_h_layer = malloc(sizeof(layer)); + l.uh = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_h_layer) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize); - l.input_h_layer->batch = batch; + *(l.uh) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.uh->batch = batch; - l.state_h_layer = malloc(sizeof(layer)); + l.wh = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.state_h_layer) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize); - l.state_h_layer->batch = batch; + *(l.wh) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wh->batch = batch; l.batch_normalize = batch_normalize; @@ -94,68 +92,80 @@ layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_no l.backward_gpu = backward_gru_layer_gpu; l.update_gpu = update_gru_layer_gpu; - l.forgot_state_gpu = cuda_make_array(l.output, batch*outputs); - l.forgot_delta_gpu = cuda_make_array(l.output, batch*outputs); - l.prev_state_gpu = cuda_make_array(l.output, batch*outputs); - l.state_gpu = cuda_make_array(l.output, batch*outputs); - l.output_gpu = cuda_make_array(l.output, batch*outputs*steps); - l.delta_gpu = cuda_make_array(l.delta, batch*outputs*steps); - l.r_gpu = cuda_make_array(l.output_gpu, batch*outputs); - l.z_gpu = cuda_make_array(l.output_gpu, batch*outputs); - l.h_gpu = cuda_make_array(l.output_gpu, batch*outputs); + l.forgot_state_gpu = cuda_make_array(0, batch*outputs); + l.forgot_delta_gpu = cuda_make_array(0, batch*outputs); + l.prev_state_gpu = cuda_make_array(0, batch*outputs); + l.state_gpu = cuda_make_array(0, batch*outputs); + l.output_gpu = cuda_make_array(0, batch*outputs*steps); + l.delta_gpu = cuda_make_array(0, batch*outputs*steps); + l.r_gpu = cuda_make_array(0, batch*outputs); + l.z_gpu = cuda_make_array(0, batch*outputs); + l.h_gpu = cuda_make_array(0, batch*outputs); + +#ifdef CUDNN + cudnnSetTensor4dDescriptor(l.uz->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.uz->out_c, l.uz->out_h, l.uz->out_w); + cudnnSetTensor4dDescriptor(l.uh->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.uh->out_c, l.uh->out_h, l.uh->out_w); + cudnnSetTensor4dDescriptor(l.ur->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.ur->out_c, l.ur->out_h, l.ur->out_w); + cudnnSetTensor4dDescriptor(l.wz->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wz->out_c, l.wz->out_h, l.wz->out_w); + cudnnSetTensor4dDescriptor(l.wh->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wh->out_c, l.wh->out_h, l.wh->out_w); + cudnnSetTensor4dDescriptor(l.wr->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wr->out_c, l.wr->out_h, l.wr->out_w); +#endif #endif return l; } -void update_gru_layer(layer l, int batch, float learning_rate, float momentum, float decay) +void update_gru_layer(layer l, update_args a) { - update_connected_layer(*(l.input_layer), batch, learning_rate, momentum, decay); - update_connected_layer(*(l.self_layer), batch, learning_rate, momentum, decay); - update_connected_layer(*(l.output_layer), batch, learning_rate, momentum, decay); + update_connected_layer(*(l.ur), a); + update_connected_layer(*(l.uz), a); + update_connected_layer(*(l.uh), a); + update_connected_layer(*(l.wr), a); + update_connected_layer(*(l.wz), a); + update_connected_layer(*(l.wh), a); } -void forward_gru_layer(layer l, network_state state) +void forward_gru_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; - layer input_z_layer = *(l.input_z_layer); - layer input_r_layer = *(l.input_r_layer); - layer input_h_layer = *(l.input_h_layer); - - layer state_z_layer = *(l.state_z_layer); - layer state_r_layer = *(l.state_r_layer); - layer state_h_layer = *(l.state_h_layer); - - fill_cpu(l.outputs * l.batch * l.steps, 0, input_z_layer.delta, 1); - fill_cpu(l.outputs * l.batch * l.steps, 0, input_r_layer.delta, 1); - fill_cpu(l.outputs * l.batch * l.steps, 0, input_h_layer.delta, 1); - - fill_cpu(l.outputs * l.batch * l.steps, 0, state_z_layer.delta, 1); - fill_cpu(l.outputs * l.batch * l.steps, 0, state_r_layer.delta, 1); - fill_cpu(l.outputs * l.batch * l.steps, 0, state_h_layer.delta, 1); - if(state.train) { + layer uz = *(l.uz); + layer ur = *(l.ur); + layer uh = *(l.uh); + + layer wz = *(l.wz); + layer wr = *(l.wr); + layer wh = *(l.wh); + + fill_cpu(l.outputs * l.batch * l.steps, 0, uz.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, ur.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, uh.delta, 1); + + fill_cpu(l.outputs * l.batch * l.steps, 0, wz.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wr.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wh.delta, 1); + if(net.train) { fill_cpu(l.outputs * l.batch * l.steps, 0, l.delta, 1); copy_cpu(l.outputs*l.batch, l.state, 1, l.prev_state, 1); } for (i = 0; i < l.steps; ++i) { s.input = l.state; - forward_connected_layer(state_z_layer, s); - forward_connected_layer(state_r_layer, s); + forward_connected_layer(wz, s); + forward_connected_layer(wr, s); - s.input = state.input; - forward_connected_layer(input_z_layer, s); - forward_connected_layer(input_r_layer, s); - forward_connected_layer(input_h_layer, s); + s.input = net.input; + forward_connected_layer(uz, s); + forward_connected_layer(ur, s); + forward_connected_layer(uh, s); - copy_cpu(l.outputs*l.batch, input_z_layer.output, 1, l.z_cpu, 1); - axpy_cpu(l.outputs*l.batch, 1, state_z_layer.output, 1, l.z_cpu, 1); + copy_cpu(l.outputs*l.batch, uz.output, 1, l.z_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, wz.output, 1, l.z_cpu, 1); - copy_cpu(l.outputs*l.batch, input_r_layer.output, 1, l.r_cpu, 1); - axpy_cpu(l.outputs*l.batch, 1, state_r_layer.output, 1, l.r_cpu, 1); + copy_cpu(l.outputs*l.batch, ur.output, 1, l.r_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, wr.output, 1, l.r_cpu, 1); activate_array(l.z_cpu, l.outputs*l.batch, LOGISTIC); activate_array(l.r_cpu, l.outputs*l.batch, LOGISTIC); @@ -164,34 +174,34 @@ void forward_gru_layer(layer l, network_state state) mul_cpu(l.outputs*l.batch, l.r_cpu, 1, l.forgot_state, 1); s.input = l.forgot_state; - forward_connected_layer(state_h_layer, s); + forward_connected_layer(wh, s); - copy_cpu(l.outputs*l.batch, input_h_layer.output, 1, l.h_cpu, 1); - axpy_cpu(l.outputs*l.batch, 1, state_h_layer.output, 1, l.h_cpu, 1); + copy_cpu(l.outputs*l.batch, uh.output, 1, l.h_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, wh.output, 1, l.h_cpu, 1); - #ifdef USET - activate_array(l.h_cpu, l.outputs*l.batch, TANH); - #else - activate_array(l.h_cpu, l.outputs*l.batch, LOGISTIC); - #endif + if(l.tanh){ + activate_array(l.h_cpu, l.outputs*l.batch, TANH); + } else { + activate_array(l.h_cpu, l.outputs*l.batch, LOGISTIC); + } weighted_sum_cpu(l.state, l.h_cpu, l.z_cpu, l.outputs*l.batch, l.output); copy_cpu(l.outputs*l.batch, l.output, 1, l.state, 1); - state.input += l.inputs*l.batch; + net.input += l.inputs*l.batch; l.output += l.outputs*l.batch; - increment_layer(&input_z_layer, 1); - increment_layer(&input_r_layer, 1); - increment_layer(&input_h_layer, 1); + increment_layer(&uz, 1); + increment_layer(&ur, 1); + increment_layer(&uh, 1); - increment_layer(&state_z_layer, 1); - increment_layer(&state_r_layer, 1); - increment_layer(&state_h_layer, 1); + increment_layer(&wz, 1); + increment_layer(&wr, 1); + increment_layer(&wh, 1); } } -void backward_gru_layer(layer l, network_state state) +void backward_gru_layer(layer l, network net) { } @@ -205,191 +215,192 @@ void push_gru_layer(layer l) { } -void update_gru_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay) +void update_gru_layer_gpu(layer l, update_args a) { - update_connected_layer_gpu(*(l.input_r_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.input_z_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.input_h_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.state_r_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.state_z_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.state_h_layer), batch, learning_rate, momentum, decay); + update_connected_layer_gpu(*(l.ur), a); + update_connected_layer_gpu(*(l.uz), a); + update_connected_layer_gpu(*(l.uh), a); + update_connected_layer_gpu(*(l.wr), a); + update_connected_layer_gpu(*(l.wz), a); + update_connected_layer_gpu(*(l.wh), a); } -void forward_gru_layer_gpu(layer l, network_state state) +void forward_gru_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = {0}; + s.train = net.train; int i; - layer input_z_layer = *(l.input_z_layer); - layer input_r_layer = *(l.input_r_layer); - layer input_h_layer = *(l.input_h_layer); - - layer state_z_layer = *(l.state_z_layer); - layer state_r_layer = *(l.state_r_layer); - layer state_h_layer = *(l.state_h_layer); - - fill_ongpu(l.outputs * l.batch * l.steps, 0, input_z_layer.delta_gpu, 1); - fill_ongpu(l.outputs * l.batch * l.steps, 0, input_r_layer.delta_gpu, 1); - fill_ongpu(l.outputs * l.batch * l.steps, 0, input_h_layer.delta_gpu, 1); - - fill_ongpu(l.outputs * l.batch * l.steps, 0, state_z_layer.delta_gpu, 1); - fill_ongpu(l.outputs * l.batch * l.steps, 0, state_r_layer.delta_gpu, 1); - fill_ongpu(l.outputs * l.batch * l.steps, 0, state_h_layer.delta_gpu, 1); - if(state.train) { - fill_ongpu(l.outputs * l.batch * l.steps, 0, l.delta_gpu, 1); - copy_ongpu(l.outputs*l.batch, l.state_gpu, 1, l.prev_state_gpu, 1); + layer uz = *(l.uz); + layer ur = *(l.ur); + layer uh = *(l.uh); + + layer wz = *(l.wz); + layer wr = *(l.wr); + layer wh = *(l.wh); + + fill_gpu(l.outputs * l.batch * l.steps, 0, uz.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, ur.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, uh.delta_gpu, 1); + + fill_gpu(l.outputs * l.batch * l.steps, 0, wz.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wr.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wh.delta_gpu, 1); + if(net.train) { + fill_gpu(l.outputs * l.batch * l.steps, 0, l.delta_gpu, 1); + copy_gpu(l.outputs*l.batch, l.state_gpu, 1, l.prev_state_gpu, 1); } for (i = 0; i < l.steps; ++i) { - s.input = l.state_gpu; - forward_connected_layer_gpu(state_z_layer, s); - forward_connected_layer_gpu(state_r_layer, s); + s.input_gpu = l.state_gpu; + forward_connected_layer_gpu(wz, s); + forward_connected_layer_gpu(wr, s); - s.input = state.input; - forward_connected_layer_gpu(input_z_layer, s); - forward_connected_layer_gpu(input_r_layer, s); - forward_connected_layer_gpu(input_h_layer, s); + s.input_gpu = net.input_gpu; + forward_connected_layer_gpu(uz, s); + forward_connected_layer_gpu(ur, s); + forward_connected_layer_gpu(uh, s); + copy_gpu(l.outputs*l.batch, uz.output_gpu, 1, l.z_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wz.output_gpu, 1, l.z_gpu, 1); - copy_ongpu(l.outputs*l.batch, input_z_layer.output_gpu, 1, l.z_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_z_layer.output_gpu, 1, l.z_gpu, 1); + copy_gpu(l.outputs*l.batch, ur.output_gpu, 1, l.r_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wr.output_gpu, 1, l.r_gpu, 1); - copy_ongpu(l.outputs*l.batch, input_r_layer.output_gpu, 1, l.r_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_r_layer.output_gpu, 1, l.r_gpu, 1); + activate_array_gpu(l.z_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.r_gpu, l.outputs*l.batch, LOGISTIC); - activate_array_ongpu(l.z_gpu, l.outputs*l.batch, LOGISTIC); - activate_array_ongpu(l.r_gpu, l.outputs*l.batch, LOGISTIC); + copy_gpu(l.outputs*l.batch, l.state_gpu, 1, l.forgot_state_gpu, 1); + mul_gpu(l.outputs*l.batch, l.r_gpu, 1, l.forgot_state_gpu, 1); - copy_ongpu(l.outputs*l.batch, l.state_gpu, 1, l.forgot_state_gpu, 1); - mul_ongpu(l.outputs*l.batch, l.r_gpu, 1, l.forgot_state_gpu, 1); + s.input_gpu = l.forgot_state_gpu; + forward_connected_layer_gpu(wh, s); - s.input = l.forgot_state_gpu; - forward_connected_layer_gpu(state_h_layer, s); + copy_gpu(l.outputs*l.batch, uh.output_gpu, 1, l.h_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wh.output_gpu, 1, l.h_gpu, 1); - copy_ongpu(l.outputs*l.batch, input_h_layer.output_gpu, 1, l.h_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_h_layer.output_gpu, 1, l.h_gpu, 1); - - #ifdef USET - activate_array_ongpu(l.h_gpu, l.outputs*l.batch, TANH); - #else - activate_array_ongpu(l.h_gpu, l.outputs*l.batch, LOGISTIC); - #endif + if(l.tanh){ + activate_array_gpu(l.h_gpu, l.outputs*l.batch, TANH); + } else { + activate_array_gpu(l.h_gpu, l.outputs*l.batch, LOGISTIC); + } weighted_sum_gpu(l.state_gpu, l.h_gpu, l.z_gpu, l.outputs*l.batch, l.output_gpu); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.state_gpu, 1); - copy_ongpu(l.outputs*l.batch, l.output_gpu, 1, l.state_gpu, 1); - - state.input += l.inputs*l.batch; + net.input_gpu += l.inputs*l.batch; l.output_gpu += l.outputs*l.batch; - increment_layer(&input_z_layer, 1); - increment_layer(&input_r_layer, 1); - increment_layer(&input_h_layer, 1); + increment_layer(&uz, 1); + increment_layer(&ur, 1); + increment_layer(&uh, 1); - increment_layer(&state_z_layer, 1); - increment_layer(&state_r_layer, 1); - increment_layer(&state_h_layer, 1); + increment_layer(&wz, 1); + increment_layer(&wr, 1); + increment_layer(&wh, 1); } } -void backward_gru_layer_gpu(layer l, network_state state) +void backward_gru_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = {0}; + s.train = net.train; int i; - layer input_z_layer = *(l.input_z_layer); - layer input_r_layer = *(l.input_r_layer); - layer input_h_layer = *(l.input_h_layer); + layer uz = *(l.uz); + layer ur = *(l.ur); + layer uh = *(l.uh); - layer state_z_layer = *(l.state_z_layer); - layer state_r_layer = *(l.state_r_layer); - layer state_h_layer = *(l.state_h_layer); + layer wz = *(l.wz); + layer wr = *(l.wr); + layer wh = *(l.wh); - increment_layer(&input_z_layer, l.steps - 1); - increment_layer(&input_r_layer, l.steps - 1); - increment_layer(&input_h_layer, l.steps - 1); + increment_layer(&uz, l.steps - 1); + increment_layer(&ur, l.steps - 1); + increment_layer(&uh, l.steps - 1); - increment_layer(&state_z_layer, l.steps - 1); - increment_layer(&state_r_layer, l.steps - 1); - increment_layer(&state_h_layer, l.steps - 1); + increment_layer(&wz, l.steps - 1); + increment_layer(&wr, l.steps - 1); + increment_layer(&wh, l.steps - 1); - state.input += l.inputs*l.batch*(l.steps-1); - if(state.delta) state.delta += l.inputs*l.batch*(l.steps-1); + net.input_gpu += l.inputs*l.batch*(l.steps-1); + if(net.delta_gpu) net.delta_gpu += l.inputs*l.batch*(l.steps-1); l.output_gpu += l.outputs*l.batch*(l.steps-1); l.delta_gpu += l.outputs*l.batch*(l.steps-1); + float *end_state = l.output_gpu; for (i = l.steps-1; i >= 0; --i) { - if(i != 0) copy_ongpu(l.outputs*l.batch, l.output_gpu - l.outputs*l.batch, 1, l.prev_state_gpu, 1); + if(i != 0) copy_gpu(l.outputs*l.batch, l.output_gpu - l.outputs*l.batch, 1, l.state_gpu, 1); + else copy_gpu(l.outputs*l.batch, l.prev_state_gpu, 1, l.state_gpu, 1); float *prev_delta_gpu = (i == 0) ? 0 : l.delta_gpu - l.outputs*l.batch; - copy_ongpu(l.outputs*l.batch, input_z_layer.output_gpu, 1, l.z_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_z_layer.output_gpu, 1, l.z_gpu, 1); - - copy_ongpu(l.outputs*l.batch, input_r_layer.output_gpu, 1, l.r_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_r_layer.output_gpu, 1, l.r_gpu, 1); - - activate_array_ongpu(l.z_gpu, l.outputs*l.batch, LOGISTIC); - activate_array_ongpu(l.r_gpu, l.outputs*l.batch, LOGISTIC); - - copy_ongpu(l.outputs*l.batch, input_h_layer.output_gpu, 1, l.h_gpu, 1); - axpy_ongpu(l.outputs*l.batch, 1, state_h_layer.output_gpu, 1, l.h_gpu, 1); - - #ifdef USET - activate_array_ongpu(l.h_gpu, l.outputs*l.batch, TANH); - #else - activate_array_ongpu(l.h_gpu, l.outputs*l.batch, LOGISTIC); - #endif - - weighted_delta_gpu(l.prev_state_gpu, l.h_gpu, l.z_gpu, prev_delta_gpu, input_h_layer.delta_gpu, input_z_layer.delta_gpu, l.outputs*l.batch, l.delta_gpu); - - #ifdef USET - gradient_array_ongpu(l.h_gpu, l.outputs*l.batch, TANH, input_h_layer.delta_gpu); - #else - gradient_array_ongpu(l.h_gpu, l.outputs*l.batch, LOGISTIC, input_h_layer.delta_gpu); - #endif - - copy_ongpu(l.outputs*l.batch, input_h_layer.delta_gpu, 1, state_h_layer.delta_gpu, 1); - - copy_ongpu(l.outputs*l.batch, l.prev_state_gpu, 1, l.forgot_state_gpu, 1); - mul_ongpu(l.outputs*l.batch, l.r_gpu, 1, l.forgot_state_gpu, 1); - fill_ongpu(l.outputs*l.batch, 0, l.forgot_delta_gpu, 1); - - s.input = l.forgot_state_gpu; - s.delta = l.forgot_delta_gpu; - - backward_connected_layer_gpu(state_h_layer, s); + copy_gpu(l.outputs*l.batch, uz.output_gpu, 1, l.z_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wz.output_gpu, 1, l.z_gpu, 1); + + copy_gpu(l.outputs*l.batch, ur.output_gpu, 1, l.r_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wr.output_gpu, 1, l.r_gpu, 1); + + activate_array_gpu(l.z_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.r_gpu, l.outputs*l.batch, LOGISTIC); + + copy_gpu(l.outputs*l.batch, uh.output_gpu, 1, l.h_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, wh.output_gpu, 1, l.h_gpu, 1); + + if(l.tanh){ + activate_array_gpu(l.h_gpu, l.outputs*l.batch, TANH); + } else { + activate_array_gpu(l.h_gpu, l.outputs*l.batch, LOGISTIC); + } + + weighted_delta_gpu(l.state_gpu, l.h_gpu, l.z_gpu, prev_delta_gpu, uh.delta_gpu, uz.delta_gpu, l.outputs*l.batch, l.delta_gpu); + + if(l.tanh){ + gradient_array_gpu(l.h_gpu, l.outputs*l.batch, TANH, uh.delta_gpu); + } else { + gradient_array_gpu(l.h_gpu, l.outputs*l.batch, LOGISTIC, uh.delta_gpu); + } + + copy_gpu(l.outputs*l.batch, uh.delta_gpu, 1, wh.delta_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.state_gpu, 1, l.forgot_state_gpu, 1); + mul_gpu(l.outputs*l.batch, l.r_gpu, 1, l.forgot_state_gpu, 1); + fill_gpu(l.outputs*l.batch, 0, l.forgot_delta_gpu, 1); + + s.input_gpu = l.forgot_state_gpu; + s.delta_gpu = l.forgot_delta_gpu; + + backward_connected_layer_gpu(wh, s); if(prev_delta_gpu) mult_add_into_gpu(l.outputs*l.batch, l.forgot_delta_gpu, l.r_gpu, prev_delta_gpu); - mult_add_into_gpu(l.outputs*l.batch, l.forgot_delta_gpu, l.prev_state_gpu, input_r_layer.delta_gpu); - - gradient_array_ongpu(l.r_gpu, l.outputs*l.batch, LOGISTIC, input_r_layer.delta_gpu); - copy_ongpu(l.outputs*l.batch, input_r_layer.delta_gpu, 1, state_r_layer.delta_gpu, 1); - - gradient_array_ongpu(l.z_gpu, l.outputs*l.batch, LOGISTIC, input_z_layer.delta_gpu); - copy_ongpu(l.outputs*l.batch, input_z_layer.delta_gpu, 1, state_z_layer.delta_gpu, 1); - - s.input = l.prev_state_gpu; - s.delta = prev_delta_gpu; - - backward_connected_layer_gpu(state_r_layer, s); - backward_connected_layer_gpu(state_z_layer, s); - - s.input = state.input; - s.delta = state.delta; - - backward_connected_layer_gpu(input_h_layer, s); - backward_connected_layer_gpu(input_r_layer, s); - backward_connected_layer_gpu(input_z_layer, s); - - - state.input -= l.inputs*l.batch; - if(state.delta) state.delta -= l.inputs*l.batch; + mult_add_into_gpu(l.outputs*l.batch, l.forgot_delta_gpu, l.state_gpu, ur.delta_gpu); + + gradient_array_gpu(l.r_gpu, l.outputs*l.batch, LOGISTIC, ur.delta_gpu); + copy_gpu(l.outputs*l.batch, ur.delta_gpu, 1, wr.delta_gpu, 1); + + gradient_array_gpu(l.z_gpu, l.outputs*l.batch, LOGISTIC, uz.delta_gpu); + copy_gpu(l.outputs*l.batch, uz.delta_gpu, 1, wz.delta_gpu, 1); + + s.input_gpu = l.state_gpu; + s.delta_gpu = prev_delta_gpu; + + backward_connected_layer_gpu(wr, s); + backward_connected_layer_gpu(wz, s); + + s.input_gpu = net.input_gpu; + s.delta_gpu = net.delta_gpu; + + backward_connected_layer_gpu(uh, s); + backward_connected_layer_gpu(ur, s); + backward_connected_layer_gpu(uz, s); + + + net.input_gpu -= l.inputs*l.batch; + if(net.delta_gpu) net.delta_gpu -= l.inputs*l.batch; l.output_gpu -= l.outputs*l.batch; l.delta_gpu -= l.outputs*l.batch; - increment_layer(&input_z_layer, -1); - increment_layer(&input_r_layer, -1); - increment_layer(&input_h_layer, -1); + increment_layer(&uz, -1); + increment_layer(&ur, -1); + increment_layer(&uh, -1); - increment_layer(&state_z_layer, -1); - increment_layer(&state_r_layer, -1); - increment_layer(&state_h_layer, -1); + increment_layer(&wz, -1); + increment_layer(&wr, -1); + increment_layer(&wh, -1); } + copy_gpu(l.outputs*l.batch, end_state, 1, l.state_gpu, 1); } #endif diff --git a/image.darknet/inst/include/darknet/src/gru_layer.h b/image.darknet/inst/include/darknet/src/gru_layer.h index 9e19cee..9067942 100644 --- a/image.darknet/inst/include/darknet/src/gru_layer.h +++ b/image.darknet/inst/include/darknet/src/gru_layer.h @@ -6,16 +6,16 @@ #include "layer.h" #include "network.h" -layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_normalize); +layer make_gru_layer(int batch, int inputs, int outputs, int steps, int batch_normalize, int adam); -void forward_gru_layer(layer l, network_state state); -void backward_gru_layer(layer l, network_state state); -void update_gru_layer(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_gru_layer(layer l, network state); +void backward_gru_layer(layer l, network state); +void update_gru_layer(layer l, update_args a); #ifdef GPU -void forward_gru_layer_gpu(layer l, network_state state); -void backward_gru_layer_gpu(layer l, network_state state); -void update_gru_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_gru_layer_gpu(layer l, network state); +void backward_gru_layer_gpu(layer l, network state); +void update_gru_layer_gpu(layer l, update_args a); void push_gru_layer(layer l); void pull_gru_layer(layer l); #endif diff --git a/image.darknet/inst/include/darknet/src/im2col.h b/image.darknet/inst/include/darknet/src/im2col.h index f0ddeee..02c4247 100644 --- a/image.darknet/inst/include/darknet/src/im2col.h +++ b/image.darknet/inst/include/darknet/src/im2col.h @@ -7,7 +7,7 @@ void im2col_cpu(float* data_im, #ifdef GPU -void im2col_ongpu(float *im, +void im2col_gpu(float *im, int channels, int height, int width, int ksize, int stride, int pad,float *data_col); diff --git a/image.darknet/inst/include/darknet/src/im2col_kernels.cu b/image.darknet/inst/include/darknet/src/im2col_kernels.cu index d42d600..07b5e67 100644 --- a/image.darknet/inst/include/darknet/src/im2col_kernels.cu +++ b/image.darknet/inst/include/darknet/src/im2col_kernels.cu @@ -45,7 +45,7 @@ __global__ void im2col_gpu_kernel(const int n, const float* data_im, } } -void im2col_ongpu(float *im, +void im2col_gpu(float *im, int channels, int height, int width, int ksize, int stride, int pad, float *data_col){ // We are going to launch channels * height_col * width_col kernels, each diff --git a/image.darknet/inst/include/darknet/src/image.c b/image.darknet/inst/include/darknet/src/image.c index 5a90efd..4a2c6ba 100644 --- a/image.darknet/inst/include/darknet/src/image.c +++ b/image.darknet/inst/include/darknet/src/image.c @@ -10,12 +10,6 @@ #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" -#ifdef OPENCV -#include "opencv2/highgui/highgui_c.h" -#include "opencv2/imgproc/imgproc_c.h" -#endif - - int windows = 0; float colors[6][3] = { {1,0,1}, {0,0,1},{0,1,1},{0,1,0},{1,1,0},{1,0,0} }; @@ -31,6 +25,70 @@ float get_color(int c, int x, int max) return r; } +image mask_to_rgb(image mask) +{ + int n = mask.c; + image im = make_image(mask.w, mask.h, 3); + int i, j; + for(j = 0; j < n; ++j){ + int offset = j*123457 % n; + float red = get_color(2,offset,n); + float green = get_color(1,offset,n); + float blue = get_color(0,offset,n); + for(i = 0; i < im.w*im.h; ++i){ + im.data[i + 0*im.w*im.h] += mask.data[j*im.h*im.w + i]*red; + im.data[i + 1*im.w*im.h] += mask.data[j*im.h*im.w + i]*green; + im.data[i + 2*im.w*im.h] += mask.data[j*im.h*im.w + i]*blue; + } + } + return im; +} + +static float get_pixel(image m, int x, int y, int c) +{ + assert(x < m.w && y < m.h && c < m.c); + return m.data[c*m.h*m.w + y*m.w + x]; +} +static float get_pixel_extend(image m, int x, int y, int c) +{ + if(x < 0 || x >= m.w || y < 0 || y >= m.h) return 0; + /* + if(x < 0) x = 0; + if(x >= m.w) x = m.w-1; + if(y < 0) y = 0; + if(y >= m.h) y = m.h-1; + */ + if(c < 0 || c >= m.c) return 0; + return get_pixel(m, x, y, c); +} +static void set_pixel(image m, int x, int y, int c, float val) +{ + if (x < 0 || y < 0 || c < 0 || x >= m.w || y >= m.h || c >= m.c) return; + assert(x < m.w && y < m.h && c < m.c); + m.data[c*m.h*m.w + y*m.w + x] = val; +} +static void add_pixel(image m, int x, int y, int c, float val) +{ + assert(x < m.w && y < m.h && c < m.c); + m.data[c*m.h*m.w + y*m.w + x] += val; +} + +static float bilinear_interpolate(image im, float x, float y, int c) +{ + int ix = (int) floorf(x); + int iy = (int) floorf(y); + + float dx = x - ix; + float dy = y - iy; + + float val = (1-dy) * (1-dx) * get_pixel_extend(im, ix, iy, c) + + dy * (1-dx) * get_pixel_extend(im, ix, iy+1, c) + + (1-dy) * dx * get_pixel_extend(im, ix+1, iy, c) + + dy * dx * get_pixel_extend(im, ix+1, iy+1, c); + return val; +} + + void composite_image(image source, image dest, int dx, int dy) { int x,y,k; @@ -73,6 +131,7 @@ image tile_images(image a, image b, int dx) image get_label(image **characters, char *string, int size) { + size = size/10; if(size > 7) size = 7; image label = make_empty_image(0,0,0); while(*string){ @@ -177,23 +236,36 @@ image **load_alphabet() return alphabets; } -void draw_detections(image im, int num, float thresh, box *boxes, float **probs, char **names, image **alphabet, int classes) +void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes) { - int i; + int i,j; for(i = 0; i < num; ++i){ - int class = max_index(probs[i], classes); - float prob = probs[i][class]; - if(prob > thresh){ - - int width = im.h * .012; - - if(0){ - width = pow(prob, 1./2.)*10+1; - alphabet = 0; + char labelstr[4096] = {0}; + int class = -1; + for(j = 0; j < classes; ++j){ + if (dets[i].prob[j] > thresh){ + if (class < 0) { + strcat(labelstr, names[j]); + class = j; + } else { + strcat(labelstr, ", "); + strcat(labelstr, names[j]); + } + printf("%s: %.0f%%\n", names[j], dets[i].prob[j]*100); } + } + if(class >= 0){ + int width = im.h * .006; - printf("%s: %.0f%%\n", names[class], prob*100); + /* + if(0){ + width = pow(prob, 1./2.)*10+1; + alphabet = 0; + } + */ + + //printf("%d %s: %.0f%%\n", i, names[class], prob*100); int offset = class*123457 % classes; float red = get_color(2,offset,classes); float green = get_color(1,offset,classes); @@ -205,7 +277,8 @@ void draw_detections(image im, int num, float thresh, box *boxes, float **probs, rgb[0] = red; rgb[1] = green; rgb[2] = blue; - box b = boxes[i]; + box b = dets[i].bbox; + //printf("%f %f %f %f\n", b.x, b.y, b.w, b.h); int left = (b.x-b.w/2.)*im.w; int right = (b.x+b.w/2.)*im.w; @@ -219,8 +292,18 @@ void draw_detections(image im, int num, float thresh, box *boxes, float **probs, draw_box_width(im, left, top, right, bot, width, red, green, blue); if (alphabet) { - image label = get_label(alphabet, names[class], (im.h*.03)/10); + image label = get_label(alphabet, labelstr, (im.h*.03)); draw_label(im, top + width, left, label, rgb); + free_image(label); + } + if (dets[i].mask){ + image mask = float_to_image(14, 14, 1, dets[i].mask); + image resized_mask = resize_image(mask, b.w*im.w, b.h*im.h); + image tmask = threshold_image(resized_mask, .5); + embed_image(tmask, im, left, top); + free_image(mask); + free_image(resized_mask); + free_image(tmask); } } } @@ -294,6 +377,54 @@ image image_distance(image a, image b) return dist; } +void ghost_image(image source, image dest, int dx, int dy) +{ + int x,y,k; + float max_dist = sqrt((-source.w/2. + .5)*(-source.w/2. + .5)); + for(k = 0; k < source.c; ++k){ + for(y = 0; y < source.h; ++y){ + for(x = 0; x < source.w; ++x){ + float dist = sqrt((x - source.w/2. + .5)*(x - source.w/2. + .5) + (y - source.h/2. + .5)*(y - source.h/2. + .5)); + float alpha = (1 - dist/max_dist); + if(alpha < 0) alpha = 0; + float v1 = get_pixel(source, x,y,k); + float v2 = get_pixel(dest, dx+x,dy+y,k); + float val = alpha*v1 + (1-alpha)*v2; + set_pixel(dest, dx+x, dy+y, k, val); + } + } + } +} + +void blocky_image(image im, int s) +{ + int i,j,k; + for(k = 0; k < im.c; ++k){ + for(j = 0; j < im.h; ++j){ + for(i = 0; i < im.w; ++i){ + im.data[i + im.w*(j + im.h*k)] = im.data[i/s*s + im.w*(j/s*s + im.h*k)]; + } + } + } +} + +void censor_image(image im, int dx, int dy, int w, int h) +{ + int i,j,k; + int s = 32; + if(dx < 0) dx = 0; + if(dy < 0) dy = 0; + + for(k = 0; k < im.c; ++k){ + for(j = dy; j < dy + h && j < im.h; ++j){ + for(i = dx; i < dx + w && i < im.w; ++i){ + im.data[i + im.w*(j + im.h*k)] = im.data[i/s*s + im.w*(j/s*s + im.h*k)]; + //im.data[i + j*im.w + k*im.w*im.h] = 0; + } + } + } +} + void embed_image(image source, image dest, int dx, int dy) { int x,y,k; @@ -380,6 +511,11 @@ void normalize_image2(image p) free(max); } +void copy_image_into(image src, image dest) +{ + memcpy(dest.data, src.data, src.h*src.w*src.c*sizeof(float)); +} + image copy_image(image p) { image copy = p; @@ -398,145 +534,27 @@ void rgbgr_image(image im) } } -#ifdef OPENCV -void show_image_cv(image p, const char *name) -{ - int x,y,k; - image copy = copy_image(p); - constrain_image(copy); - if(p.c == 3) rgbgr_image(copy); - //normalize_image(copy); - - char buff[256]; - //sprintf(buff, "%s (%d)", name, windows); - sprintf(buff, "%s", name); - - IplImage *disp = cvCreateImage(cvSize(p.w,p.h), IPL_DEPTH_8U, p.c); - int step = disp->widthStep; - cvNamedWindow(buff, CV_WINDOW_NORMAL); - //cvMoveWindow(buff, 100*(windows%10) + 200*(windows/10), 100*(windows%10)); - ++windows; - for(y = 0; y < p.h; ++y){ - for(x = 0; x < p.w; ++x){ - for(k= 0; k < p.c; ++k){ - disp->imageData[y*step + x*p.c + k] = (unsigned char)(get_pixel(copy,x,y,k)*255); - } - } - } - free_image(copy); - if(0){ - int w = 448; - int h = w*p.h/p.w; - if(h > 1000){ - h = 1000; - w = h*p.w/p.h; - } - IplImage *buffer = disp; - disp = cvCreateImage(cvSize(w, h), buffer->depth, buffer->nChannels); - cvResize(buffer, disp, CV_INTER_LINEAR); - cvReleaseImage(&buffer); - } - cvShowImage(buff, disp); - cvReleaseImage(&disp); -} -#endif - -void show_image(image p, const char *name) +int show_image(image p, const char *name, int ms) { #ifdef OPENCV - show_image_cv(p, name); + int c = show_image_cv(p, name, ms); + return c; #else fprintf(stderr, "Not compiled with OpenCV, saving to %s.png instead\n", name); save_image(p, name); + return -1; #endif } -#ifdef OPENCV - -image ipl_to_image(IplImage* src) -{ - unsigned char *data = (unsigned char *)src->imageData; - int h = src->height; - int w = src->width; - int c = src->nChannels; - int step = src->widthStep; - image out = make_image(w, h, c); - int i, j, k, count=0;; - - for(k= 0; k < c; ++k){ - for(i = 0; i < h; ++i){ - for(j = 0; j < w; ++j){ - out.data[count++] = data[i*step + j*c + k]/255.; - } - } - } - return out; -} - -image load_image_cv(char *filename, int channels) -{ - IplImage* src = 0; - int flag = -1; - if (channels == 0) flag = -1; - else if (channels == 1) flag = 0; - else if (channels == 3) flag = 1; - else { - fprintf(stderr, "OpenCV can't force load with %d channels\n", channels); - } - - if( (src = cvLoadImage(filename, flag)) == 0 ) - { - fprintf(stderr, "Cannot load image \"%s\"\n", filename); - char buff[256]; - sprintf(buff, "echo %s >> bad.list", filename); - system(buff); - return make_image(10,10,3); - //exit(0); - } - image out = ipl_to_image(src); - cvReleaseImage(&src); - rgbgr_image(out); - return out; -} - -image get_image_from_stream(CvCapture *cap) -{ - IplImage* src = cvQueryFrame(cap); - if (!src) return make_empty_image(0,0,0); - image im = ipl_to_image(src); - rgbgr_image(im); - return im; -} - -void save_image_jpg(image p, const char *name) -{ - image copy = copy_image(p); - if(p.c == 3) rgbgr_image(copy); - int x,y,k; - - char buff[256]; - sprintf(buff, "%s.jpg", name); - - IplImage *disp = cvCreateImage(cvSize(p.w,p.h), IPL_DEPTH_8U, p.c); - int step = disp->widthStep; - for(y = 0; y < p.h; ++y){ - for(x = 0; x < p.w; ++x){ - for(k= 0; k < p.c; ++k){ - disp->imageData[y*step + x*p.c + k] = (unsigned char)(get_pixel(copy,x,y,k)*255); - } - } - } - cvSaveImage(buff, disp,0); - cvReleaseImage(&disp); - free_image(copy); -} -#endif - -void save_image_png(image im, const char *name) +void save_image_options(image im, const char *name, IMTYPE f, int quality) { char buff[256]; //sprintf(buff, "%s (%d)", name, windows); - sprintf(buff, "%s.png", name); + if(f == PNG) sprintf(buff, "%s.png", name); + else if (f == BMP) sprintf(buff, "%s.bmp", name); + else if (f == TGA) sprintf(buff, "%s.tga", name); + else if (f == JPG) sprintf(buff, "%s.jpg", name); + else sprintf(buff, "%s.png", name); unsigned char *data = calloc(im.w*im.h*im.c, sizeof(char)); int i,k; for(k = 0; k < im.c; ++k){ @@ -544,21 +562,20 @@ void save_image_png(image im, const char *name) data[i*im.c+k] = (unsigned char) (255*im.data[i + k*im.w*im.h]); } } - int success = stbi_write_png(buff, im.w, im.h, im.c, data, im.w*im.c); + int success = 0; + if(f == PNG) success = stbi_write_png(buff, im.w, im.h, im.c, data, im.w*im.c); + else if (f == BMP) success = stbi_write_bmp(buff, im.w, im.h, im.c, data); + else if (f == TGA) success = stbi_write_tga(buff, im.w, im.h, im.c, data); + else if (f == JPG) success = stbi_write_jpg(buff, im.w, im.h, im.c, data, quality); free(data); if(!success) fprintf(stderr, "Failed to write image %s\n", buff); } void save_image(image im, const char *name) { -#ifdef OPENCV - save_image_jpg(im, name); -#else - save_image_png(im, name); -#endif + save_image_options(im, name, JPG, 80); } - void show_image_layers(image p, char *name) { int i; @@ -566,7 +583,7 @@ void show_image_layers(image p, char *name) for(i = 0; i < p.c; ++i){ sprintf(buff, "%s - Layer %d", name, i); image layer = get_image_layer(p, i); - show_image(layer, buff); + show_image(layer, buff, 1); free_image(layer); } } @@ -574,7 +591,7 @@ void show_image_layers(image p, char *name) void show_image_collapsed(image p, char *name) { image c = collapse_image_layers(p, 1); - show_image(c, name); + show_image(c, name, 1); free_image(c); } @@ -613,6 +630,29 @@ image float_to_image(int w, int h, int c, float *data) return out; } +void place_image(image im, int w, int h, int dx, int dy, image canvas) +{ + int x, y, c; + for(c = 0; c < im.c; ++c){ + for(y = 0; y < h; ++y){ + for(x = 0; x < w; ++x){ + float rx = ((float)x / w) * im.w; + float ry = ((float)y / h) * im.h; + float val = bilinear_interpolate(im, rx, ry, c); + set_pixel(canvas, x + dx, y + dy, c, val); + } + } + } +} + +image center_crop_image(image im, int w, int h) +{ + int m = (im.w < im.h) ? im.w : im.h; + image c = crop_image(im, (im.w - m) / 2, (im.h - m)/2, m, m); + image r = resize_image(c, w, h); + free_image(c); + return r; +} image rotate_crop_image(image im, float rad, float s, int w, int h, float dx, float dy, float aspect) { @@ -652,6 +692,12 @@ image rotate_image(image im, float rad) return rot; } +void fill_image(image m, float s) +{ + int i; + for(i = 0; i < m.h*m.w*m.c; ++i) m.data[i] = s; +} + void translate_image(image m, float s) { int i; @@ -676,9 +722,7 @@ image crop_image(image im, int dx, int dy, int w, int h) float val = 0; r = constrain_int(r, 0, im.h-1); c = constrain_int(c, 0, im.w-1); - if (r >= 0 && r < im.h && c >= 0 && c < im.w) { - val = get_pixel(im, c, r, k); - } + val = get_pixel(im, c, r, k); set_pixel(cropped, i, j, k, val); } } @@ -746,11 +790,44 @@ void composite_3d(char *f1, char *f2, char *out, int delta) for(i = 0; i < c.w*c.h; ++i){ c.data[i] = a.data[i]; } -#ifdef OPENCV - save_image_jpg(c, out); -#else save_image(c, out); -#endif +} + +void letterbox_image_into(image im, int w, int h, image boxed) +{ + int new_w = im.w; + int new_h = im.h; + if (((float)w/im.w) < ((float)h/im.h)) { + new_w = w; + new_h = (im.h * w)/im.w; + } else { + new_h = h; + new_w = (im.w * h)/im.h; + } + image resized = resize_image(im, new_w, new_h); + embed_image(resized, boxed, (w-new_w)/2, (h-new_h)/2); + free_image(resized); +} + +image letterbox_image(image im, int w, int h) +{ + int new_w = im.w; + int new_h = im.h; + if (((float)w/im.w) < ((float)h/im.h)) { + new_w = w; + new_h = (im.h * w)/im.w; + } else { + new_h = h; + new_w = (im.w * h)/im.h; + } + image resized = resize_image(im, new_w, new_h); + image boxed = make_image(w, h, im.c); + fill_image(boxed, .5); + //int i; + //for(i = 0; i < boxed.w*boxed.h*boxed.c; ++i) boxed.data[i] = 0; + embed_image(resized, boxed, (w-new_w)/2, (h-new_h)/2); + free_image(resized); + return boxed; } image resize_max(image im, int max) @@ -793,8 +870,9 @@ image random_crop_image(image im, int w, int h) return crop; } -image random_augment_image(image im, float angle, float aspect, int low, int high, int size) +augment_args random_augment_args(image im, float angle, float aspect, int low, int high, int w, int h) { + augment_args a = {0}; aspect = rand_scale(aspect); int r = rand_int(low, high); int min = (im.h < im.w*aspect) ? im.h : im.w*aspect; @@ -802,15 +880,27 @@ image random_augment_image(image im, float angle, float aspect, int low, int hig float rad = rand_uniform(-angle, angle) * TWO_PI / 360.; - float dx = (im.w*scale/aspect - size) / 2.; - float dy = (im.h*scale - size) / 2.; - if(dx < 0) dx = 0; - if(dy < 0) dy = 0; + float dx = (im.w*scale/aspect - w) / 2.; + float dy = (im.h*scale - w) / 2.; + //if(dx < 0) dx = 0; + //if(dy < 0) dy = 0; dx = rand_uniform(-dx, dx); dy = rand_uniform(-dy, dy); - image crop = rotate_crop_image(im, rad, scale, size, size, dx, dy, aspect); + a.rad = rad; + a.scale = scale; + a.w = w; + a.h = h; + a.dx = dx; + a.dy = dy; + a.aspect = aspect; + return a; +} +image random_augment_image(image im, float angle, float aspect, int low, int high, int w, int h) +{ + augment_args a = random_augment_args(im, angle, aspect, low, high, w, h); + image crop = rotate_crop_image(im, a.rad, a.scale, a.w, a.h, a.dx, a.dy, a.aspect); return crop; } @@ -824,6 +914,52 @@ float three_way_min(float a, float b, float c) return (a < b) ? ( (a < c) ? a : c) : ( (b < c) ? b : c) ; } +void yuv_to_rgb(image im) +{ + assert(im.c == 3); + int i, j; + float r, g, b; + float y, u, v; + for(j = 0; j < im.h; ++j){ + for(i = 0; i < im.w; ++i){ + y = get_pixel(im, i , j, 0); + u = get_pixel(im, i , j, 1); + v = get_pixel(im, i , j, 2); + + r = y + 1.13983*v; + g = y + -.39465*u + -.58060*v; + b = y + 2.03211*u; + + set_pixel(im, i, j, 0, r); + set_pixel(im, i, j, 1, g); + set_pixel(im, i, j, 2, b); + } + } +} + +void rgb_to_yuv(image im) +{ + assert(im.c == 3); + int i, j; + float r, g, b; + float y, u, v; + for(j = 0; j < im.h; ++j){ + for(i = 0; i < im.w; ++i){ + r = get_pixel(im, i , j, 0); + g = get_pixel(im, i , j, 1); + b = get_pixel(im, i , j, 2); + + y = .299*r + .587*g + .114*b; + u = -.14713*r + -.28886*g + .436*b; + v = .615*r + -.51499*g + -.10001*b; + + set_pixel(im, i, j, 0, y); + set_pixel(im, i, j, 1, u); + set_pixel(im, i, j, 2, v); + } + } +} + // http://www.cs.rit.edu/~ncs/color/t_convert.html void rgb_to_hsv(image im) { @@ -903,12 +1039,30 @@ void hsv_to_rgb(image im) } } +void grayscale_image_3c(image im) +{ + assert(im.c == 3); + int i, j, k; + float scale[] = {0.299, 0.587, 0.114}; + for(j = 0; j < im.h; ++j){ + for(i = 0; i < im.w; ++i){ + float val = 0; + for(k = 0; k < 3; ++k){ + val += scale[k]*get_pixel(im, i, j, k); + } + im.data[0*im.h*im.w + im.w*j + i] = val; + im.data[1*im.h*im.w + im.w*j + i] = val; + im.data[2*im.h*im.w + im.w*j + i] = val; + } + } +} + image grayscale_image(image im) { assert(im.c == 3); int i, j, k; image gray = make_image(im.w, im.h, 1); - float scale[] = {0.587, 0.299, 0.114}; + float scale[] = {0.299, 0.587, 0.114}; for(k = 0; k < im.c; ++k){ for(j = 0; j < im.h; ++j){ for(i = 0; i < im.w; ++i){ @@ -1042,21 +1196,6 @@ void saturate_exposure_image(image im, float sat, float exposure) constrain_image(im); } -float bilinear_interpolate(image im, float x, float y, int c) -{ - int ix = (int) floorf(x); - int iy = (int) floorf(y); - - float dx = x - ix; - float dy = y - iy; - - float val = (1-dy) * (1-dx) * get_pixel_extend(im, ix, iy, c) + - dy * (1-dx) * get_pixel_extend(im, ix, iy+1, c) + - (1-dy) * dx * get_pixel_extend(im, ix+1, iy, c) + - dy * dx * get_pixel_extend(im, ix+1, iy+1, c); - return val; -} - image resize_image(image im, int w, int h) { image resized = make_image(w, h, im.c); @@ -1119,16 +1258,16 @@ void test_resize(char *filename) distort_image(c4, .1, .66666, 1.5); - show_image(im, "Original"); - show_image(gray, "Gray"); - show_image(c1, "C1"); - show_image(c2, "C2"); - show_image(c3, "C3"); - show_image(c4, "C4"); + show_image(im, "Original", 1); + show_image(gray, "Gray", 1); + show_image(c1, "C1", 1); + show_image(c2, "C2", 1); + show_image(c3, "C3", 1); + show_image(c4, "C4", 1); #ifdef OPENCV while(1){ - image aug = random_augment_image(im, 0, .75, 320, 448, 320); - show_image(aug, "aug"); + image aug = random_augment_image(im, 0, .75, 320, 448, 320, 320); + show_image(aug, "aug", 1); free_image(aug); @@ -1143,10 +1282,9 @@ void test_resize(char *filename) float dhue = rand_uniform(-hue, hue); distort_image(c, dhue, dsat, dexp); - show_image(c, "rand"); + show_image(c, "rand", 1); printf("%f %f %f\n", dhue, dsat, dexp); free_image(c); - cvWaitKey(0); } #endif } @@ -1206,33 +1344,6 @@ image get_image_layer(image m, int l) } return out; } - -float get_pixel(image m, int x, int y, int c) -{ - assert(x < m.w && y < m.h && c < m.c); - return m.data[c*m.h*m.w + y*m.w + x]; -} -float get_pixel_extend(image m, int x, int y, int c) -{ - if(x < 0) x = 0; - if(x >= m.w) x = m.w-1; - if(y < 0) y = 0; - if(y >= m.h) y = m.h-1; - if(c < 0 || c >= m.c) return 0; - return get_pixel(m, x, y, c); -} -void set_pixel(image m, int x, int y, int c, float val) -{ - if (x < 0 || y < 0 || c < 0 || x >= m.w || y >= m.h || c >= m.c) return; - assert(x < m.w && y < m.h && c < m.c); - m.data[c*m.h*m.w + y*m.w + x] = val; -} -void add_pixel(image m, int x, int y, int c, float val) -{ - assert(x < m.w && y < m.h && c < m.c); - m.data[c*m.h*m.w + y*m.w + x] += val; -} - void print_image(image m) { int i, j, k; @@ -1325,7 +1436,7 @@ void show_image_normalized(image im, const char *name) { image c = copy_image(im); normalize_image(c); - show_image(c, name); + show_image(c, name, 1); free_image(c); } @@ -1343,7 +1454,7 @@ void show_images(image *ims, int n, char *window) */ normalize_image(m); save_image(m, window); - show_image(m, window); + show_image(m, window, 1); free_image(m); } diff --git a/image.darknet/inst/include/darknet/src/image.h b/image.darknet/inst/include/darknet/src/image.h index 39c3962..3392bb9 100644 --- a/image.darknet/inst/include/darknet/src/image.h +++ b/image.darknet/inst/include/darknet/src/image.h @@ -7,81 +7,63 @@ #include #include #include "box.h" +#include "darknet.h" -typedef struct { - int h; - int w; - int c; - float *data; -} image; +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef OPENCV +void *open_video_stream(const char *f, int c, int w, int h, int fps); +image get_image_from_stream(void *p); +image load_image_cv(char *filename, int channels); +int show_image_cv(image im, const char* name, int ms); +#endif float get_color(int c, int x, int max); -void flip_image(image a); void draw_box(image a, int x1, int y1, int x2, int y2, float r, float g, float b); -void draw_box_width(image a, int x1, int y1, int x2, int y2, int w, float r, float g, float b); void draw_bbox(image a, box bbox, int w, float r, float g, float b); -void draw_label(image a, int r, int c, image label, const float *rgb); void write_label(image a, int r, int c, image *characters, char *string, float *rgb); -void draw_detections(image im, int num, float thresh, box *boxes, float **probs, char **names, image **labels, int classes); image image_distance(image a, image b); void scale_image(image m, float s); -image crop_image(image im, int dx, int dy, int w, int h); +image rotate_crop_image(image im, float rad, float s, int w, int h, float dx, float dy, float aspect); image random_crop_image(image im, int w, int h); -image random_augment_image(image im, float angle, float aspect, int low, int high, int size); -void random_distort_image(image im, float hue, float saturation, float exposure); -image resize_image(image im, int w, int h); -image resize_min(image im, int min); +image random_augment_image(image im, float angle, float aspect, int low, int high, int w, int h); +augment_args random_augment_args(image im, float angle, float aspect, int low, int high, int w, int h); +void letterbox_image_into(image im, int w, int h, image boxed); image resize_max(image im, int max); void translate_image(image m, float s); -void normalize_image(image p); -image rotate_image(image m, float rad); -void rotate_image_cw(image im, int times); void embed_image(image source, image dest, int dx, int dy); +void place_image(image im, int w, int h, int dx, int dy, image canvas); void saturate_image(image im, float sat); void exposure_image(image im, float sat); void distort_image(image im, float hue, float sat, float val); void saturate_exposure_image(image im, float sat, float exposure); +void rgb_to_hsv(image im); void hsv_to_rgb(image im); -void rgbgr_image(image im); -void constrain_image(image im); -void composite_3d(char *f1, char *f2, char *out, int delta); -int best_3d_shift_r(image a, image b, int min, int max); +void yuv_to_rgb(image im); +void rgb_to_yuv(image im); -image grayscale_image(image im); -image threshold_image(image im, float thresh); image collapse_image_layers(image source, int border); image collapse_images_horz(image *ims, int n); image collapse_images_vert(image *ims, int n); -void show_image(image p, const char *name); void show_image_normalized(image im, const char *name); -void save_image_png(image im, const char *name); -void save_image(image p, const char *name); void show_images(image *ims, int n, char *window); void show_image_layers(image p, char *name); void show_image_collapsed(image p, char *name); void print_image(image m); -image make_image(int w, int h, int c); -image make_random_image(int w, int h, int c); image make_empty_image(int w, int h, int c); -image float_to_image(int w, int h, int c, float *data); -image copy_image(image p); -image load_image(char *filename, int w, int h, int c); -image load_image_color(char *filename, int w, int h); -image **load_alphabet(); - -float get_pixel(image m, int x, int y, int c); -float get_pixel_extend(image m, int x, int y, int c); -void set_pixel(image m, int x, int y, int c, float val); -void add_pixel(image m, int x, int y, int c, float val); -float bilinear_interpolate(image im, float x, float y, int c); +void copy_image_into(image src, image dest); image get_image_layer(image m, int l); -void free_image(image m); -void test_resize(char *filename); +#ifdef __cplusplus +} +#endif + #endif diff --git a/image.darknet/inst/include/darknet/src/image_opencv.cpp b/image.darknet/inst/include/darknet/src/image_opencv.cpp new file mode 100644 index 0000000..7511280 --- /dev/null +++ b/image.darknet/inst/include/darknet/src/image_opencv.cpp @@ -0,0 +1,135 @@ +#ifdef OPENCV + +#include "stdio.h" +#include "stdlib.h" +#include "opencv2/opencv.hpp" +#include "image.h" + +using namespace cv; + +extern "C" { + +IplImage *image_to_ipl(image im) +{ + int x,y,c; + IplImage *disp = cvCreateImage(cvSize(im.w,im.h), IPL_DEPTH_8U, im.c); + int step = disp->widthStep; + for(y = 0; y < im.h; ++y){ + for(x = 0; x < im.w; ++x){ + for(c= 0; c < im.c; ++c){ + float val = im.data[c*im.h*im.w + y*im.w + x]; + disp->imageData[y*step + x*im.c + c] = (unsigned char)(val*255); + } + } + } + return disp; +} + +image ipl_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; +} + +Mat image_to_mat(image im) +{ + image copy = copy_image(im); + constrain_image(copy); + if(im.c == 3) rgbgr_image(copy); + + IplImage *ipl = image_to_ipl(copy); + Mat m = cvarrToMat(ipl, true); + cvReleaseImage(&ipl); + free_image(copy); + return m; +} + +image mat_to_image(Mat m) +{ + IplImage ipl = m; + image im = ipl_to_image(&ipl); + rgbgr_image(im); + return im; +} + +void *open_video_stream(const char *f, int c, int w, int h, int fps) +{ + VideoCapture *cap; + if(f) cap = new VideoCapture(f); + else cap = new VideoCapture(c); + if(!cap->isOpened()) return 0; + if(w) cap->set(CV_CAP_PROP_FRAME_WIDTH, w); + if(h) cap->set(CV_CAP_PROP_FRAME_HEIGHT, w); + if(fps) cap->set(CV_CAP_PROP_FPS, w); + return (void *) cap; +} + +image get_image_from_stream(void *p) +{ + VideoCapture *cap = (VideoCapture *)p; + Mat m; + *cap >> m; + if(m.empty()) return make_empty_image(0,0,0); + return mat_to_image(m); +} + +image load_image_cv(char *filename, int channels) +{ + int flag = -1; + if (channels == 0) flag = -1; + else if (channels == 1) flag = 0; + else if (channels == 3) flag = 1; + else { + fprintf(stderr, "OpenCV can't force load with %d channels\n", channels); + } + Mat m; + m = imread(filename, flag); + if(!m.data){ + fprintf(stderr, "Cannot load image \"%s\"\n", filename); + char buff[256]; + sprintf(buff, "echo %s >> bad.list", filename); + system(buff); + return make_image(10,10,3); + //exit(0); + } + image im = mat_to_image(m); + return im; +} + +int show_image_cv(image im, const char* name, int ms) +{ + Mat m = image_to_mat(im); + imshow(name, m); + int c = waitKey(ms); + if (c != -1) c = c%256; + return c; +} + +void make_window(char *name, int w, int h, int fullscreen) +{ + namedWindow(name, WINDOW_NORMAL); + if (fullscreen) { + setWindowProperty(name, CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN); + } else { + resizeWindow(name, w, h); + if(strcmp(name, "Demo") == 0) moveWindow(name, 0, 0); + } +} + +} + +#endif diff --git a/image.darknet/inst/include/darknet/src/iseg_layer.c b/image.darknet/inst/include/darknet/src/iseg_layer.c new file mode 100644 index 0000000..2bf03a8 --- /dev/null +++ b/image.darknet/inst/include/darknet/src/iseg_layer.c @@ -0,0 +1,225 @@ +#include "iseg_layer.h" +#include "activations.h" +#include "blas.h" +#include "box.h" +#include "cuda.h" +#include "utils.h" + +#include +#include +#include +#include + +layer make_iseg_layer(int batch, int w, int h, int classes, int ids) +{ + layer l = {0}; + l.type = ISEG; + + l.h = h; + l.w = w; + l.c = classes + ids; + l.out_w = l.w; + l.out_h = l.h; + l.out_c = l.c; + l.classes = classes; + l.batch = batch; + l.extra = ids; + l.cost = calloc(1, sizeof(float)); + l.outputs = h*w*l.c; + l.inputs = l.outputs; + l.truths = 90*(l.w*l.h+1); + l.delta = calloc(batch*l.outputs, sizeof(float)); + l.output = calloc(batch*l.outputs, sizeof(float)); + + l.counts = calloc(90, sizeof(int)); + l.sums = calloc(90, sizeof(float*)); + if(ids){ + int i; + for(i = 0; i < 90; ++i){ + l.sums[i] = calloc(ids, sizeof(float)); + } + } + + l.forward = forward_iseg_layer; + l.backward = backward_iseg_layer; +#ifdef GPU + l.forward_gpu = forward_iseg_layer_gpu; + l.backward_gpu = backward_iseg_layer_gpu; + l.output_gpu = cuda_make_array(l.output, batch*l.outputs); + l.delta_gpu = cuda_make_array(l.delta, batch*l.outputs); +#endif + + fprintf(stderr, "iseg\n"); + srand(0); + + return l; +} + +void resize_iseg_layer(layer *l, int w, int h) +{ + l->w = w; + l->h = h; + + l->outputs = h*w*l->c; + l->inputs = l->outputs; + + l->output = realloc(l->output, l->batch*l->outputs*sizeof(float)); + l->delta = realloc(l->delta, l->batch*l->outputs*sizeof(float)); + +#ifdef GPU + cuda_free(l->delta_gpu); + cuda_free(l->output_gpu); + + l->delta_gpu = cuda_make_array(l->delta, l->batch*l->outputs); + l->output_gpu = cuda_make_array(l->output, l->batch*l->outputs); +#endif +} + +void forward_iseg_layer(const layer l, network net) +{ + + double time = what_time_is_it_now(); + int i,b,j,k; + int ids = l.extra; + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); + memset(l.delta, 0, l.outputs * l.batch * sizeof(float)); + +#ifndef GPU + for (b = 0; b < l.batch; ++b){ + int index = b*l.outputs; + activate_array(l.output + index, l.classes*l.w*l.h, LOGISTIC); + } +#endif + + for (b = 0; b < l.batch; ++b){ + // a priori, each pixel has no class + for(i = 0; i < l.classes; ++i){ + for(k = 0; k < l.w*l.h; ++k){ + int index = b*l.outputs + i*l.w*l.h + k; + l.delta[index] = 0 - l.output[index]; + } + } + + // a priori, embedding should be small magnitude + for(i = 0; i < ids; ++i){ + for(k = 0; k < l.w*l.h; ++k){ + int index = b*l.outputs + (i+l.classes)*l.w*l.h + k; + l.delta[index] = .1 * (0 - l.output[index]); + } + } + + + memset(l.counts, 0, 90*sizeof(int)); + for(i = 0; i < 90; ++i){ + fill_cpu(ids, 0, l.sums[i], 1); + + int c = net.truth[b*l.truths + i*(l.w*l.h+1)]; + if(c < 0) break; + // add up metric embeddings for each instance + for(k = 0; k < l.w*l.h; ++k){ + int index = b*l.outputs + c*l.w*l.h + k; + float v = net.truth[b*l.truths + i*(l.w*l.h + 1) + 1 + k]; + if(v){ + l.delta[index] = v - l.output[index]; + axpy_cpu(ids, 1, l.output + b*l.outputs + l.classes*l.w*l.h + k, l.w*l.h, l.sums[i], 1); + ++l.counts[i]; + } + } + } + + float *mse = calloc(90, sizeof(float)); + for(i = 0; i < 90; ++i){ + int c = net.truth[b*l.truths + i*(l.w*l.h+1)]; + if(c < 0) break; + for(k = 0; k < l.w*l.h; ++k){ + float v = net.truth[b*l.truths + i*(l.w*l.h + 1) + 1 + k]; + if(v){ + int z; + float sum = 0; + for(z = 0; z < ids; ++z){ + int index = b*l.outputs + (l.classes + z)*l.w*l.h + k; + sum += pow(l.sums[i][z]/l.counts[i] - l.output[index], 2); + } + mse[i] += sum; + } + } + mse[i] /= l.counts[i]; + } + + // Calculate average embedding + for(i = 0; i < 90; ++i){ + if(!l.counts[i]) continue; + scal_cpu(ids, 1.f/l.counts[i], l.sums[i], 1); + if(b == 0 && net.gpu_index == 0){ + printf("%4d, %6.3f, ", l.counts[i], mse[i]); + for(j = 0; j < ids; ++j){ + printf("%6.3f,", l.sums[i][j]); + } + printf("\n"); + } + } + free(mse); + + // Calculate embedding loss + for(i = 0; i < 90; ++i){ + if(!l.counts[i]) continue; + for(k = 0; k < l.w*l.h; ++k){ + float v = net.truth[b*l.truths + i*(l.w*l.h + 1) + 1 + k]; + if(v){ + for(j = 0; j < 90; ++j){ + if(!l.counts[j])continue; + int z; + for(z = 0; z < ids; ++z){ + int index = b*l.outputs + (l.classes + z)*l.w*l.h + k; + float diff = l.sums[j][z] - l.output[index]; + if (j == i) l.delta[index] += diff < 0? -.1 : .1; + else l.delta[index] += -(diff < 0? -.1 : .1); + } + } + } + } + } + + for(i = 0; i < ids; ++i){ + for(k = 0; k < l.w*l.h; ++k){ + int index = b*l.outputs + (i+l.classes)*l.w*l.h + k; + l.delta[index] *= .01; + } + } + } + + *(l.cost) = pow(mag_array(l.delta, l.outputs * l.batch), 2); + printf("took %lf sec\n", what_time_is_it_now() - time); +} + +void backward_iseg_layer(const layer l, network net) +{ + axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, net.delta, 1); +} + +#ifdef GPU + +void forward_iseg_layer_gpu(const layer l, network net) +{ + copy_gpu(l.batch*l.inputs, net.input_gpu, 1, l.output_gpu, 1); + int b; + for (b = 0; b < l.batch; ++b){ + activate_array_gpu(l.output_gpu + b*l.outputs, l.classes*l.w*l.h, LOGISTIC); + //if(l.extra) activate_array_gpu(l.output_gpu + b*l.outputs + l.classes*l.w*l.h, l.extra*l.w*l.h, LOGISTIC); + } + + cuda_pull_array(l.output_gpu, net.input, l.batch*l.inputs); + forward_iseg_layer(l, net); + cuda_push_array(l.delta_gpu, l.delta, l.batch*l.outputs); +} + +void backward_iseg_layer_gpu(const layer l, network net) +{ + int b; + for (b = 0; b < l.batch; ++b){ + //if(l.extra) gradient_array_gpu(l.output_gpu + b*l.outputs + l.classes*l.w*l.h, l.extra*l.w*l.h, LOGISTIC, l.delta_gpu + b*l.outputs + l.classes*l.w*l.h); + } + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); +} +#endif + diff --git a/image.darknet/inst/include/darknet/src/iseg_layer.h b/image.darknet/inst/include/darknet/src/iseg_layer.h new file mode 100644 index 0000000..dd8e64e --- /dev/null +++ b/image.darknet/inst/include/darknet/src/iseg_layer.h @@ -0,0 +1,19 @@ +#ifndef ISEG_LAYER_H +#define ISEG_LAYER_H + +#include "darknet.h" +#include "layer.h" +#include "network.h" + +layer make_iseg_layer(int batch, int w, int h, int classes, int ids); +void forward_iseg_layer(const layer l, network net); +void backward_iseg_layer(const layer l, network net); +void resize_iseg_layer(layer *l, int w, int h); +int iseg_num_detections(layer l, float thresh); + +#ifdef GPU +void forward_iseg_layer_gpu(const layer l, network net); +void backward_iseg_layer_gpu(layer l, network net); +#endif + +#endif diff --git a/image.darknet/inst/include/darknet/src/l2norm_layer.c b/image.darknet/inst/include/darknet/src/l2norm_layer.c new file mode 100644 index 0000000..d099479 --- /dev/null +++ b/image.darknet/inst/include/darknet/src/l2norm_layer.c @@ -0,0 +1,63 @@ +#include "l2norm_layer.h" +#include "activations.h" +#include "blas.h" +#include "cuda.h" + +#include +#include +#include +#include +#include + +layer make_l2norm_layer(int batch, int inputs) +{ + fprintf(stderr, "l2norm %4d\n", inputs); + layer l = {0}; + l.type = L2NORM; + l.batch = batch; + l.inputs = inputs; + l.outputs = inputs; + l.output = calloc(inputs*batch, sizeof(float)); + l.scales = calloc(inputs*batch, sizeof(float)); + l.delta = calloc(inputs*batch, sizeof(float)); + + l.forward = forward_l2norm_layer; + l.backward = backward_l2norm_layer; + #ifdef GPU + l.forward_gpu = forward_l2norm_layer_gpu; + l.backward_gpu = backward_l2norm_layer_gpu; + + l.output_gpu = cuda_make_array(l.output, inputs*batch); + l.scales_gpu = cuda_make_array(l.output, inputs*batch); + l.delta_gpu = cuda_make_array(l.delta, inputs*batch); + #endif + return l; +} + +void forward_l2norm_layer(const layer l, network net) +{ + copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); + l2normalize_cpu(l.output, l.scales, l.batch, l.out_c, l.out_w*l.out_h); +} + +void backward_l2norm_layer(const layer l, network net) +{ + //axpy_cpu(l.inputs*l.batch, 1, l.scales, 1, l.delta, 1); + axpy_cpu(l.inputs*l.batch, 1, l.delta, 1, net.delta, 1); +} + +#ifdef GPU + +void forward_l2norm_layer_gpu(const layer l, network net) +{ + copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + l2normalize_gpu(l.output_gpu, l.scales_gpu, l.batch, l.out_c, l.out_w*l.out_h); +} + +void backward_l2norm_layer_gpu(const layer l, network net) +{ + axpy_gpu(l.batch*l.inputs, 1, l.scales_gpu, 1, l.delta_gpu, 1); + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); +} + +#endif diff --git a/image.darknet/inst/include/darknet/src/l2norm_layer.h b/image.darknet/inst/include/darknet/src/l2norm_layer.h new file mode 100644 index 0000000..1ca6f71 --- /dev/null +++ b/image.darknet/inst/include/darknet/src/l2norm_layer.h @@ -0,0 +1,15 @@ +#ifndef L2NORM_LAYER_H +#define L2NORM_LAYER_H +#include "layer.h" +#include "network.h" + +layer make_l2norm_layer(int batch, int inputs); +void forward_l2norm_layer(const layer l, network net); +void backward_l2norm_layer(const layer l, network net); + +#ifdef GPU +void forward_l2norm_layer_gpu(const layer l, network net); +void backward_l2norm_layer_gpu(const layer l, network net); +#endif + +#endif diff --git a/image.darknet/inst/include/darknet/src/layer.c b/image.darknet/inst/include/darknet/src/layer.c index 622cf26..c27b477 100644 --- a/image.darknet/inst/include/darknet/src/layer.c +++ b/image.darknet/inst/include/darknet/src/layer.c @@ -1,5 +1,6 @@ #include "layer.h" #include "cuda.h" + #include void free_layer(layer l) @@ -32,7 +33,6 @@ void free_layer(layer l) if(l.scale_updates) free(l.scale_updates); if(l.weights) free(l.weights); if(l.weight_updates) free(l.weight_updates); - if(l.col_image) free(l.col_image); if(l.delta) free(l.delta); if(l.output) free(l.output); if(l.squared) free(l.squared); @@ -80,7 +80,6 @@ void free_layer(layer l) if(l.rolling_variance_gpu) cuda_free(l.rolling_variance_gpu); if(l.variance_delta_gpu) cuda_free(l.variance_delta_gpu); if(l.mean_delta_gpu) cuda_free(l.mean_delta_gpu); - if(l.col_image_gpu) cuda_free(l.col_image_gpu); if(l.x_gpu) cuda_free(l.x_gpu); if(l.x_norm_gpu) cuda_free(l.x_norm_gpu); if(l.weights_gpu) cuda_free(l.weights_gpu); diff --git a/image.darknet/inst/include/darknet/src/layer.h b/image.darknet/inst/include/darknet/src/layer.h index 806542b..af6cd2a 100644 --- a/image.darknet/inst/include/darknet/src/layer.h +++ b/image.darknet/inst/include/darknet/src/layer.h @@ -1,271 +1 @@ -#ifndef BASE_LAYER_H -#define BASE_LAYER_H - -#include "activations.h" -#include "stddef.h" -#include "tree.h" - -struct network_state; - -struct layer; -typedef struct layer layer; - -typedef enum { - CONVOLUTIONAL, - DECONVOLUTIONAL, - CONNECTED, - MAXPOOL, - SOFTMAX, - DETECTION, - DROPOUT, - CROP, - ROUTE, - COST, - NORMALIZATION, - AVGPOOL, - LOCAL, - SHORTCUT, - ACTIVE, - RNN, - GRU, - CRNN, - BATCHNORM, - NETWORK, - XNOR, - REGION, - REORG, - BLANK -} LAYER_TYPE; - -typedef enum{ - SSE, MASKED, SMOOTH -} COST_TYPE; - -struct layer{ - LAYER_TYPE type; - ACTIVATION activation; - COST_TYPE cost_type; - void (*forward) (struct layer, struct network_state); - void (*backward) (struct layer, struct network_state); - void (*update) (struct layer, int, float, float, float); - void (*forward_gpu) (struct layer, struct network_state); - void (*backward_gpu) (struct layer, struct network_state); - void (*update_gpu) (struct layer, int, float, float, float); - int batch_normalize; - int shortcut; - int batch; - int forced; - int flipped; - int inputs; - int outputs; - int truths; - int h,w,c; - int out_h, out_w, out_c; - int n; - int max_boxes; - int groups; - int size; - int side; - int stride; - int reverse; - int pad; - int sqrt; - int flip; - int index; - int binary; - int xnor; - int steps; - int hidden; - float dot; - float angle; - float jitter; - float saturation; - float exposure; - float shift; - float ratio; - int softmax; - int classes; - int coords; - int background; - int rescore; - int objectness; - int does_cost; - int joint; - int noadjust; - int reorg; - int log; - - int adam; - float B1; - float B2; - float eps; - int t; - - float alpha; - float beta; - float kappa; - - float coord_scale; - float object_scale; - float noobject_scale; - float class_scale; - int bias_match; - int random; - float thresh; - int classfix; - int absolute; - - int dontload; - int dontloadscales; - - float temperature; - float probability; - float scale; - - char * cweights; - int * indexes; - int * input_layers; - int * input_sizes; - int * map; - float * rand; - float * cost; - float * state; - float * prev_state; - float * forgot_state; - float * forgot_delta; - float * state_delta; - - float * concat; - float * concat_delta; - - float * binary_weights; - - float * biases; - float * bias_updates; - - float * scales; - float * scale_updates; - - float * weights; - float * weight_updates; - - float * col_image; - float * delta; - float * output; - float * squared; - float * norms; - - float * spatial_mean; - float * mean; - float * variance; - - float * mean_delta; - float * variance_delta; - - float * rolling_mean; - float * rolling_variance; - - float * x; - float * x_norm; - - float * m; - float * v; - - float * z_cpu; - float * r_cpu; - float * h_cpu; - - float * binary_input; - - struct layer *input_layer; - struct layer *self_layer; - struct layer *output_layer; - - struct layer *input_gate_layer; - struct layer *state_gate_layer; - struct layer *input_save_layer; - struct layer *state_save_layer; - struct layer *input_state_layer; - struct layer *state_state_layer; - - struct layer *input_z_layer; - struct layer *state_z_layer; - - struct layer *input_r_layer; - struct layer *state_r_layer; - - struct layer *input_h_layer; - struct layer *state_h_layer; - - tree *softmax_tree; - - size_t workspace_size; - - #ifdef GPU - int *indexes_gpu; - - float *z_gpu; - float *r_gpu; - float *h_gpu; - - float *m_gpu; - float *v_gpu; - - float * prev_state_gpu; - float * forgot_state_gpu; - float * forgot_delta_gpu; - float * state_gpu; - float * state_delta_gpu; - float * gate_gpu; - float * gate_delta_gpu; - float * save_gpu; - float * save_delta_gpu; - float * concat_gpu; - float * concat_delta_gpu; - - float *binary_input_gpu; - float *binary_weights_gpu; - - float * mean_gpu; - float * variance_gpu; - - float * rolling_mean_gpu; - float * rolling_variance_gpu; - - float * variance_delta_gpu; - float * mean_delta_gpu; - - float * col_image_gpu; - - float * x_gpu; - float * x_norm_gpu; - float * weights_gpu; - float * weight_updates_gpu; - - float * biases_gpu; - float * bias_updates_gpu; - - float * scales_gpu; - float * scale_updates_gpu; - - float * output_gpu; - float * delta_gpu; - float * rand_gpu; - float * squared_gpu; - float * norms_gpu; - #ifdef CUDNN - cudnnTensorDescriptor_t srcTensorDesc, dstTensorDesc; - cudnnTensorDescriptor_t dsrcTensorDesc, ddstTensorDesc; - cudnnFilterDescriptor_t weightDesc; - cudnnFilterDescriptor_t dweightDesc; - cudnnConvolutionDescriptor_t convDesc; - cudnnConvolutionFwdAlgo_t fw_algo; - cudnnConvolutionBwdDataAlgo_t bd_algo; - cudnnConvolutionBwdFilterAlgo_t bf_algo; - #endif - #endif -}; - -void free_layer(layer); - -#endif +#include "darknet.h" diff --git a/image.darknet/inst/include/darknet/src/list.h b/image.darknet/inst/include/darknet/src/list.h index fb818c2..6b445c7 100644 --- a/image.darknet/inst/include/darknet/src/list.h +++ b/image.darknet/inst/include/darknet/src/list.h @@ -1,26 +1,13 @@ #ifndef LIST_H #define LIST_H - -typedef struct node{ - void *val; - struct node *next; - struct node *prev; -} node; - -typedef struct list{ - int size; - node *front; - node *back; -} list; +#include "darknet.h" list *make_list(); int list_find(list *l, void *val); void list_insert(list *, void *); -void **list_to_array(list *l); -void free_list(list *l); void free_list_contents(list *l); #endif diff --git a/image.darknet/inst/include/darknet/src/local_layer.c b/image.darknet/inst/include/darknet/src/local_layer.c index 31f0ca6..74f6910 100644 --- a/image.darknet/inst/include/darknet/src/local_layer.c +++ b/image.darknet/inst/include/darknet/src/local_layer.c @@ -57,9 +57,10 @@ local_layer make_local_layer(int batch, int h, int w, int c, int n, int size, in float scale = sqrt(2./(size*size*c)); for(i = 0; i < c*n*size*size; ++i) l.weights[i] = scale*rand_uniform(-1,1); - l.col_image = calloc(out_h*out_w*size*size*c, sizeof(float)); l.output = calloc(l.batch*out_h * out_w * n, sizeof(float)); l.delta = calloc(l.batch*out_h * out_w * n, sizeof(float)); + + l.workspace_size = out_h*out_w*size*size*c; l.forward = forward_local_layer; l.backward = backward_local_layer; @@ -76,7 +77,6 @@ local_layer make_local_layer(int batch, int h, int w, int c, int n, int size, in l.biases_gpu = cuda_make_array(l.biases, l.outputs); l.bias_updates_gpu = cuda_make_array(l.bias_updates, l.outputs); - l.col_image_gpu = cuda_make_array(l.col_image, out_h*out_w*size*size*c); l.delta_gpu = cuda_make_array(l.delta, l.batch*out_h*out_w*n); l.output_gpu = cuda_make_array(l.output, l.batch*out_h*out_w*n); @@ -88,7 +88,7 @@ local_layer make_local_layer(int batch, int h, int w, int c, int n, int size, in return l; } -void forward_local_layer(const local_layer l, network_state state) +void forward_local_layer(const local_layer l, network net) { int out_h = local_out_height(l); int out_w = local_out_width(l); @@ -100,13 +100,13 @@ void forward_local_layer(const local_layer l, network_state state) } for(i = 0; i < l.batch; ++i){ - float *input = state.input + i*l.w*l.h*l.c; + float *input = net.input + i*l.w*l.h*l.c; im2col_cpu(input, l.c, l.h, l.w, - l.size, l.stride, l.pad, l.col_image); + l.size, l.stride, l.pad, net.workspace); float *output = l.output + i*l.outputs; for(j = 0; j < locations; ++j){ float *a = l.weights + j*l.size*l.size*l.c*l.n; - float *b = l.col_image + j; + float *b = net.workspace + j; float *c = output + j; int m = l.n; @@ -119,7 +119,7 @@ void forward_local_layer(const local_layer l, network_state state) activate_array(l.output, l.outputs*l.batch, l.activation); } -void backward_local_layer(local_layer l, network_state state) +void backward_local_layer(local_layer l, network net) { int i, j; int locations = l.out_w*l.out_h; @@ -131,13 +131,13 @@ void backward_local_layer(local_layer l, network_state state) } for(i = 0; i < l.batch; ++i){ - float *input = state.input + i*l.w*l.h*l.c; + float *input = net.input + i*l.w*l.h*l.c; im2col_cpu(input, l.c, l.h, l.w, - l.size, l.stride, l.pad, l.col_image); + l.size, l.stride, l.pad, net.workspace); for(j = 0; j < locations; ++j){ float *a = l.delta + i*l.outputs + j; - float *b = l.col_image + j; + float *b = net.workspace + j; float *c = l.weight_updates + j*l.size*l.size*l.c*l.n; int m = l.n; int n = l.size*l.size*l.c; @@ -146,11 +146,11 @@ void backward_local_layer(local_layer l, network_state state) gemm(0,1,m,n,k,1,a,locations,b,locations,1,c,n); } - if(state.delta){ + if(net.delta){ for(j = 0; j < locations; ++j){ float *a = l.weights + j*l.size*l.size*l.c*l.n; float *b = l.delta + i*l.outputs + j; - float *c = l.col_image + j; + float *c = net.workspace + j; int m = l.size*l.size*l.c; int n = 1; @@ -159,13 +159,18 @@ void backward_local_layer(local_layer l, network_state state) gemm(1,0,m,n,k,1,a,m,b,locations,0,c,locations); } - col2im_cpu(l.col_image, l.c, l.h, l.w, l.size, l.stride, l.pad, state.delta+i*l.c*l.h*l.w); + col2im_cpu(net.workspace, l.c, l.h, l.w, l.size, l.stride, l.pad, net.delta+i*l.c*l.h*l.w); } } } -void update_local_layer(local_layer l, int batch, float learning_rate, float momentum, float decay) +void update_local_layer(local_layer l, update_args a) { + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + int locations = l.out_w*l.out_h; int size = l.size*l.size*l.c*l.n*locations; axpy_cpu(l.outputs, learning_rate/batch, l.bias_updates, 1, l.biases, 1); @@ -178,7 +183,7 @@ void update_local_layer(local_layer l, int batch, float learning_rate, float mom #ifdef GPU -void forward_local_layer_gpu(const local_layer l, network_state state) +void forward_local_layer_gpu(const local_layer l, network net) { int out_h = local_out_height(l); int out_w = local_out_width(l); @@ -186,83 +191,88 @@ void forward_local_layer_gpu(const local_layer l, network_state state) int locations = out_h * out_w; for(i = 0; i < l.batch; ++i){ - copy_ongpu(l.outputs, l.biases_gpu, 1, l.output_gpu + i*l.outputs, 1); + copy_gpu(l.outputs, l.biases_gpu, 1, l.output_gpu + i*l.outputs, 1); } for(i = 0; i < l.batch; ++i){ - float *input = state.input + i*l.w*l.h*l.c; - im2col_ongpu(input, l.c, l.h, l.w, - l.size, l.stride, l.pad, l.col_image_gpu); + float *input = net.input_gpu + i*l.w*l.h*l.c; + im2col_gpu(input, l.c, l.h, l.w, + l.size, l.stride, l.pad, net.workspace); float *output = l.output_gpu + i*l.outputs; for(j = 0; j < locations; ++j){ float *a = l.weights_gpu + j*l.size*l.size*l.c*l.n; - float *b = l.col_image_gpu + j; + float *b = net.workspace + j; float *c = output + j; int m = l.n; int n = 1; int k = l.size*l.size*l.c; - gemm_ongpu(0,0,m,n,k,1,a,k,b,locations,1,c,locations); + gemm_gpu(0,0,m,n,k,1,a,k,b,locations,1,c,locations); } } - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); } -void backward_local_layer_gpu(local_layer l, network_state state) +void backward_local_layer_gpu(local_layer l, network net) { int i, j; int locations = l.out_w*l.out_h; - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); for(i = 0; i < l.batch; ++i){ - axpy_ongpu(l.outputs, 1, l.delta_gpu + i*l.outputs, 1, l.bias_updates_gpu, 1); + axpy_gpu(l.outputs, 1, l.delta_gpu + i*l.outputs, 1, l.bias_updates_gpu, 1); } for(i = 0; i < l.batch; ++i){ - float *input = state.input + i*l.w*l.h*l.c; - im2col_ongpu(input, l.c, l.h, l.w, - l.size, l.stride, l.pad, l.col_image_gpu); + float *input = net.input_gpu + i*l.w*l.h*l.c; + im2col_gpu(input, l.c, l.h, l.w, + l.size, l.stride, l.pad, net.workspace); for(j = 0; j < locations; ++j){ float *a = l.delta_gpu + i*l.outputs + j; - float *b = l.col_image_gpu + j; + float *b = net.workspace + j; float *c = l.weight_updates_gpu + j*l.size*l.size*l.c*l.n; int m = l.n; int n = l.size*l.size*l.c; int k = 1; - gemm_ongpu(0,1,m,n,k,1,a,locations,b,locations,1,c,n); + gemm_gpu(0,1,m,n,k,1,a,locations,b,locations,1,c,n); } - if(state.delta){ + if(net.delta_gpu){ for(j = 0; j < locations; ++j){ float *a = l.weights_gpu + j*l.size*l.size*l.c*l.n; float *b = l.delta_gpu + i*l.outputs + j; - float *c = l.col_image_gpu + j; + float *c = net.workspace + j; int m = l.size*l.size*l.c; int n = 1; int k = l.n; - gemm_ongpu(1,0,m,n,k,1,a,m,b,locations,0,c,locations); + gemm_gpu(1,0,m,n,k,1,a,m,b,locations,0,c,locations); } - col2im_ongpu(l.col_image_gpu, l.c, l.h, l.w, l.size, l.stride, l.pad, state.delta+i*l.c*l.h*l.w); + col2im_gpu(net.workspace, l.c, l.h, l.w, l.size, l.stride, l.pad, net.delta_gpu+i*l.c*l.h*l.w); } } } -void update_local_layer_gpu(local_layer l, int batch, float learning_rate, float momentum, float decay) +void update_local_layer_gpu(local_layer l, update_args a) { + float learning_rate = a.learning_rate*l.learning_rate_scale; + float momentum = a.momentum; + float decay = a.decay; + int batch = a.batch; + int locations = l.out_w*l.out_h; int size = l.size*l.size*l.c*l.n*locations; - axpy_ongpu(l.outputs, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); - scal_ongpu(l.outputs, momentum, l.bias_updates_gpu, 1); + axpy_gpu(l.outputs, learning_rate/batch, l.bias_updates_gpu, 1, l.biases_gpu, 1); + scal_gpu(l.outputs, momentum, l.bias_updates_gpu, 1); - axpy_ongpu(size, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); - axpy_ongpu(size, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); - scal_ongpu(size, momentum, l.weight_updates_gpu, 1); + axpy_gpu(size, -decay*batch, l.weights_gpu, 1, l.weight_updates_gpu, 1); + axpy_gpu(size, learning_rate/batch, l.weight_updates_gpu, 1, l.weights_gpu, 1); + scal_gpu(size, momentum, l.weight_updates_gpu, 1); } void pull_local_layer(local_layer l) diff --git a/image.darknet/inst/include/darknet/src/local_layer.h b/image.darknet/inst/include/darknet/src/local_layer.h index 28915d8..776e572 100644 --- a/image.darknet/inst/include/darknet/src/local_layer.h +++ b/image.darknet/inst/include/darknet/src/local_layer.h @@ -10,9 +10,9 @@ typedef layer local_layer; #ifdef GPU -void forward_local_layer_gpu(local_layer layer, network_state state); -void backward_local_layer_gpu(local_layer layer, network_state state); -void update_local_layer_gpu(local_layer layer, int batch, float learning_rate, float momentum, float decay); +void forward_local_layer_gpu(local_layer layer, network net); +void backward_local_layer_gpu(local_layer layer, network net); +void update_local_layer_gpu(local_layer layer, update_args a); void push_local_layer(local_layer layer); void pull_local_layer(local_layer layer); @@ -20,9 +20,9 @@ void pull_local_layer(local_layer layer); local_layer make_local_layer(int batch, int h, int w, int c, int n, int size, int stride, int pad, ACTIVATION activation); -void forward_local_layer(const local_layer layer, network_state state); -void backward_local_layer(local_layer layer, network_state state); -void update_local_layer(local_layer layer, int batch, float learning_rate, float momentum, float decay); +void forward_local_layer(const local_layer layer, network net); +void backward_local_layer(local_layer layer, network net); +void update_local_layer(local_layer layer, update_args a); void bias_output(float *output, float *biases, int batch, int n, int size); void backward_bias(float *bias_updates, float *delta, int batch, int n, int size); diff --git a/image.darknet/inst/include/darknet/src/logistic_layer.c b/image.darknet/inst/include/darknet/src/logistic_layer.c new file mode 100644 index 0000000..b2b3d6b --- /dev/null +++ b/image.darknet/inst/include/darknet/src/logistic_layer.c @@ -0,0 +1,71 @@ +#include "logistic_layer.h" +#include "activations.h" +#include "blas.h" +#include "cuda.h" + +#include +#include +#include +#include +#include + +layer make_logistic_layer(int batch, int inputs) +{ + fprintf(stderr, "logistic x entropy %4d\n", inputs); + layer l = {0}; + l.type = LOGXENT; + l.batch = batch; + l.inputs = inputs; + l.outputs = inputs; + l.loss = calloc(inputs*batch, sizeof(float)); + l.output = calloc(inputs*batch, sizeof(float)); + l.delta = calloc(inputs*batch, sizeof(float)); + l.cost = calloc(1, sizeof(float)); + + l.forward = forward_logistic_layer; + l.backward = backward_logistic_layer; + #ifdef GPU + l.forward_gpu = forward_logistic_layer_gpu; + l.backward_gpu = backward_logistic_layer_gpu; + + l.output_gpu = cuda_make_array(l.output, inputs*batch); + l.loss_gpu = cuda_make_array(l.loss, inputs*batch); + l.delta_gpu = cuda_make_array(l.delta, inputs*batch); + #endif + return l; +} + +void forward_logistic_layer(const layer l, network net) +{ + copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); + activate_array(l.output, l.outputs*l.batch, LOGISTIC); + if(net.truth){ + logistic_x_ent_cpu(l.batch*l.inputs, l.output, net.truth, l.delta, l.loss); + l.cost[0] = sum_array(l.loss, l.batch*l.inputs); + } +} + +void backward_logistic_layer(const layer l, network net) +{ + axpy_cpu(l.inputs*l.batch, 1, l.delta, 1, net.delta, 1); +} + +#ifdef GPU + +void forward_logistic_layer_gpu(const layer l, network net) +{ + copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, LOGISTIC); + if(net.truth){ + logistic_x_ent_gpu(l.batch*l.inputs, l.output_gpu, net.truth_gpu, l.delta_gpu, l.loss_gpu); + cuda_pull_array(l.loss_gpu, l.loss, l.batch*l.inputs); + l.cost[0] = sum_array(l.loss, l.batch*l.inputs); + } +} + +void backward_logistic_layer_gpu(const layer l, network net) +{ + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); +} + +#endif diff --git a/image.darknet/inst/include/darknet/src/logistic_layer.h b/image.darknet/inst/include/darknet/src/logistic_layer.h new file mode 100644 index 0000000..9c25bee --- /dev/null +++ b/image.darknet/inst/include/darknet/src/logistic_layer.h @@ -0,0 +1,15 @@ +#ifndef LOGISTIC_LAYER_H +#define LOGISTIC_LAYER_H +#include "layer.h" +#include "network.h" + +layer make_logistic_layer(int batch, int inputs); +void forward_logistic_layer(const layer l, network net); +void backward_logistic_layer(const layer l, network net); + +#ifdef GPU +void forward_logistic_layer_gpu(const layer l, network net); +void backward_logistic_layer_gpu(const layer l, network net); +#endif + +#endif diff --git a/image.darknet/inst/include/darknet/src/lstm_layer.c b/image.darknet/inst/include/darknet/src/lstm_layer.c new file mode 100644 index 0000000..fb07de2 --- /dev/null +++ b/image.darknet/inst/include/darknet/src/lstm_layer.c @@ -0,0 +1,626 @@ +#include "lstm_layer.h" +#include "connected_layer.h" +#include "utils.h" +#include "cuda.h" +#include "blas.h" +#include "gemm.h" + +#include +#include +#include +#include + +static void increment_layer(layer *l, int steps) +{ + int num = l->outputs*l->batch*steps; + l->output += num; + l->delta += num; + l->x += num; + l->x_norm += num; + +#ifdef GPU + l->output_gpu += num; + l->delta_gpu += num; + l->x_gpu += num; + l->x_norm_gpu += num; +#endif +} + +layer make_lstm_layer(int batch, int inputs, int outputs, int steps, int batch_normalize, int adam) +{ + fprintf(stderr, "LSTM Layer: %d inputs, %d outputs\n", inputs, outputs); + batch = batch / steps; + layer l = { 0 }; + l.batch = batch; + l.type = LSTM; + l.steps = steps; + l.inputs = inputs; + + l.uf = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.uf) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.uf->batch = batch; + + l.ui = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.ui) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.ui->batch = batch; + + l.ug = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.ug) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.ug->batch = batch; + + l.uo = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.uo) = make_connected_layer(batch*steps, inputs, outputs, LINEAR, batch_normalize, adam); + l.uo->batch = batch; + + l.wf = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.wf) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wf->batch = batch; + + l.wi = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.wi) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wi->batch = batch; + + l.wg = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.wg) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wg->batch = batch; + + l.wo = malloc(sizeof(layer)); + fprintf(stderr, "\t\t"); + *(l.wo) = make_connected_layer(batch*steps, outputs, outputs, LINEAR, batch_normalize, adam); + l.wo->batch = batch; + + l.batch_normalize = batch_normalize; + l.outputs = outputs; + + l.output = calloc(outputs*batch*steps, sizeof(float)); + l.state = calloc(outputs*batch, sizeof(float)); + + l.forward = forward_lstm_layer; + l.update = update_lstm_layer; + + l.prev_state_cpu = calloc(batch*outputs, sizeof(float)); + l.prev_cell_cpu = calloc(batch*outputs, sizeof(float)); + l.cell_cpu = calloc(batch*outputs*steps, sizeof(float)); + + l.f_cpu = calloc(batch*outputs, sizeof(float)); + l.i_cpu = calloc(batch*outputs, sizeof(float)); + l.g_cpu = calloc(batch*outputs, sizeof(float)); + l.o_cpu = calloc(batch*outputs, sizeof(float)); + l.c_cpu = calloc(batch*outputs, sizeof(float)); + l.h_cpu = calloc(batch*outputs, sizeof(float)); + l.temp_cpu = calloc(batch*outputs, sizeof(float)); + l.temp2_cpu = calloc(batch*outputs, sizeof(float)); + l.temp3_cpu = calloc(batch*outputs, sizeof(float)); + l.dc_cpu = calloc(batch*outputs, sizeof(float)); + l.dh_cpu = calloc(batch*outputs, sizeof(float)); + +#ifdef GPU + l.forward_gpu = forward_lstm_layer_gpu; + l.backward_gpu = backward_lstm_layer_gpu; + l.update_gpu = update_lstm_layer_gpu; + + l.output_gpu = cuda_make_array(0, batch*outputs*steps); + l.delta_gpu = cuda_make_array(0, batch*l.outputs*steps); + + l.prev_state_gpu = cuda_make_array(0, batch*outputs); + l.prev_cell_gpu = cuda_make_array(0, batch*outputs); + l.cell_gpu = cuda_make_array(0, batch*outputs*steps); + + l.f_gpu = cuda_make_array(0, batch*outputs); + l.i_gpu = cuda_make_array(0, batch*outputs); + l.g_gpu = cuda_make_array(0, batch*outputs); + l.o_gpu = cuda_make_array(0, batch*outputs); + l.c_gpu = cuda_make_array(0, batch*outputs); + l.h_gpu = cuda_make_array(0, batch*outputs); + l.temp_gpu = cuda_make_array(0, batch*outputs); + l.temp2_gpu = cuda_make_array(0, batch*outputs); + l.temp3_gpu = cuda_make_array(0, batch*outputs); + l.dc_gpu = cuda_make_array(0, batch*outputs); + l.dh_gpu = cuda_make_array(0, batch*outputs); +#ifdef CUDNN + cudnnSetTensor4dDescriptor(l.wf->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wf->out_c, l.wf->out_h, l.wf->out_w); + cudnnSetTensor4dDescriptor(l.wi->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wi->out_c, l.wi->out_h, l.wi->out_w); + cudnnSetTensor4dDescriptor(l.wg->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wg->out_c, l.wg->out_h, l.wg->out_w); + cudnnSetTensor4dDescriptor(l.wo->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.wo->out_c, l.wo->out_h, l.wo->out_w); + + cudnnSetTensor4dDescriptor(l.uf->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.uf->out_c, l.uf->out_h, l.uf->out_w); + cudnnSetTensor4dDescriptor(l.ui->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.ui->out_c, l.ui->out_h, l.ui->out_w); + cudnnSetTensor4dDescriptor(l.ug->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.ug->out_c, l.ug->out_h, l.ug->out_w); + cudnnSetTensor4dDescriptor(l.uo->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.uo->out_c, l.uo->out_h, l.uo->out_w); +#endif + +#endif + + return l; +} + +void update_lstm_layer(layer l, update_args a) +{ + update_connected_layer(*(l.wf), a); + update_connected_layer(*(l.wi), a); + update_connected_layer(*(l.wg), a); + update_connected_layer(*(l.wo), a); + update_connected_layer(*(l.uf), a); + update_connected_layer(*(l.ui), a); + update_connected_layer(*(l.ug), a); + update_connected_layer(*(l.uo), a); +} + +void forward_lstm_layer(layer l, network state) +{ + network s = { 0 }; + s.train = state.train; + int i; + layer wf = *(l.wf); + layer wi = *(l.wi); + layer wg = *(l.wg); + layer wo = *(l.wo); + + layer uf = *(l.uf); + layer ui = *(l.ui); + layer ug = *(l.ug); + layer uo = *(l.uo); + + fill_cpu(l.outputs * l.batch * l.steps, 0, wf.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wi.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wg.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, wo.delta, 1); + + fill_cpu(l.outputs * l.batch * l.steps, 0, uf.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, ui.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, ug.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, uo.delta, 1); + if (state.train) { + fill_cpu(l.outputs * l.batch * l.steps, 0, l.delta, 1); + } + + for (i = 0; i < l.steps; ++i) { + s.input = l.h_cpu; + forward_connected_layer(wf, s); + forward_connected_layer(wi, s); + forward_connected_layer(wg, s); + forward_connected_layer(wo, s); + + s.input = state.input; + forward_connected_layer(uf, s); + forward_connected_layer(ui, s); + forward_connected_layer(ug, s); + forward_connected_layer(uo, s); + + copy_cpu(l.outputs*l.batch, wf.output, 1, l.f_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, uf.output, 1, l.f_cpu, 1); + + copy_cpu(l.outputs*l.batch, wi.output, 1, l.i_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, ui.output, 1, l.i_cpu, 1); + + copy_cpu(l.outputs*l.batch, wg.output, 1, l.g_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, ug.output, 1, l.g_cpu, 1); + + copy_cpu(l.outputs*l.batch, wo.output, 1, l.o_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, uo.output, 1, l.o_cpu, 1); + + activate_array(l.f_cpu, l.outputs*l.batch, LOGISTIC); + activate_array(l.i_cpu, l.outputs*l.batch, LOGISTIC); + activate_array(l.g_cpu, l.outputs*l.batch, TANH); + activate_array(l.o_cpu, l.outputs*l.batch, LOGISTIC); + + copy_cpu(l.outputs*l.batch, l.i_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.g_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.f_cpu, 1, l.c_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, l.temp_cpu, 1, l.c_cpu, 1); + + copy_cpu(l.outputs*l.batch, l.c_cpu, 1, l.h_cpu, 1); + activate_array(l.h_cpu, l.outputs*l.batch, TANH); + mul_cpu(l.outputs*l.batch, l.o_cpu, 1, l.h_cpu, 1); + + copy_cpu(l.outputs*l.batch, l.c_cpu, 1, l.cell_cpu, 1); + copy_cpu(l.outputs*l.batch, l.h_cpu, 1, l.output, 1); + + state.input += l.inputs*l.batch; + l.output += l.outputs*l.batch; + l.cell_cpu += l.outputs*l.batch; + + increment_layer(&wf, 1); + increment_layer(&wi, 1); + increment_layer(&wg, 1); + increment_layer(&wo, 1); + + increment_layer(&uf, 1); + increment_layer(&ui, 1); + increment_layer(&ug, 1); + increment_layer(&uo, 1); + } +} + +void backward_lstm_layer(layer l, network state) +{ + network s = { 0 }; + s.train = state.train; + int i; + layer wf = *(l.wf); + layer wi = *(l.wi); + layer wg = *(l.wg); + layer wo = *(l.wo); + + layer uf = *(l.uf); + layer ui = *(l.ui); + layer ug = *(l.ug); + layer uo = *(l.uo); + + increment_layer(&wf, l.steps - 1); + increment_layer(&wi, l.steps - 1); + increment_layer(&wg, l.steps - 1); + increment_layer(&wo, l.steps - 1); + + increment_layer(&uf, l.steps - 1); + increment_layer(&ui, l.steps - 1); + increment_layer(&ug, l.steps - 1); + increment_layer(&uo, l.steps - 1); + + state.input += l.inputs*l.batch*(l.steps - 1); + if (state.delta) state.delta += l.inputs*l.batch*(l.steps - 1); + + l.output += l.outputs*l.batch*(l.steps - 1); + l.cell_cpu += l.outputs*l.batch*(l.steps - 1); + l.delta += l.outputs*l.batch*(l.steps - 1); + + for (i = l.steps - 1; i >= 0; --i) { + if (i != 0) copy_cpu(l.outputs*l.batch, l.cell_cpu - l.outputs*l.batch, 1, l.prev_cell_cpu, 1); + copy_cpu(l.outputs*l.batch, l.cell_cpu, 1, l.c_cpu, 1); + if (i != 0) copy_cpu(l.outputs*l.batch, l.output - l.outputs*l.batch, 1, l.prev_state_cpu, 1); + copy_cpu(l.outputs*l.batch, l.output, 1, l.h_cpu, 1); + + l.dh_cpu = (i == 0) ? 0 : l.delta - l.outputs*l.batch; + + copy_cpu(l.outputs*l.batch, wf.output, 1, l.f_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, uf.output, 1, l.f_cpu, 1); + + copy_cpu(l.outputs*l.batch, wi.output, 1, l.i_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, ui.output, 1, l.i_cpu, 1); + + copy_cpu(l.outputs*l.batch, wg.output, 1, l.g_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, ug.output, 1, l.g_cpu, 1); + + copy_cpu(l.outputs*l.batch, wo.output, 1, l.o_cpu, 1); + axpy_cpu(l.outputs*l.batch, 1, uo.output, 1, l.o_cpu, 1); + + activate_array(l.f_cpu, l.outputs*l.batch, LOGISTIC); + activate_array(l.i_cpu, l.outputs*l.batch, LOGISTIC); + activate_array(l.g_cpu, l.outputs*l.batch, TANH); + activate_array(l.o_cpu, l.outputs*l.batch, LOGISTIC); + + copy_cpu(l.outputs*l.batch, l.delta, 1, l.temp3_cpu, 1); + + copy_cpu(l.outputs*l.batch, l.c_cpu, 1, l.temp_cpu, 1); + activate_array(l.temp_cpu, l.outputs*l.batch, TANH); + + copy_cpu(l.outputs*l.batch, l.temp3_cpu, 1, l.temp2_cpu, 1); + mul_cpu(l.outputs*l.batch, l.o_cpu, 1, l.temp2_cpu, 1); + + gradient_array(l.temp_cpu, l.outputs*l.batch, TANH, l.temp2_cpu); + axpy_cpu(l.outputs*l.batch, 1, l.dc_cpu, 1, l.temp2_cpu, 1); + + copy_cpu(l.outputs*l.batch, l.c_cpu, 1, l.temp_cpu, 1); + activate_array(l.temp_cpu, l.outputs*l.batch, TANH); + mul_cpu(l.outputs*l.batch, l.temp3_cpu, 1, l.temp_cpu, 1); + gradient_array(l.o_cpu, l.outputs*l.batch, LOGISTIC, l.temp_cpu); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, wo.delta, 1); + s.input = l.prev_state_cpu; + s.delta = l.dh_cpu; + backward_connected_layer(wo, s); + + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, uo.delta, 1); + s.input = state.input; + s.delta = state.delta; + backward_connected_layer(uo, s); + + copy_cpu(l.outputs*l.batch, l.temp2_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.i_cpu, 1, l.temp_cpu, 1); + gradient_array(l.g_cpu, l.outputs*l.batch, TANH, l.temp_cpu); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, wg.delta, 1); + s.input = l.prev_state_cpu; + s.delta = l.dh_cpu; + backward_connected_layer(wg, s); + + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, ug.delta, 1); + s.input = state.input; + s.delta = state.delta; + backward_connected_layer(ug, s); + + copy_cpu(l.outputs*l.batch, l.temp2_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.g_cpu, 1, l.temp_cpu, 1); + gradient_array(l.i_cpu, l.outputs*l.batch, LOGISTIC, l.temp_cpu); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, wi.delta, 1); + s.input = l.prev_state_cpu; + s.delta = l.dh_cpu; + backward_connected_layer(wi, s); + + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, ui.delta, 1); + s.input = state.input; + s.delta = state.delta; + backward_connected_layer(ui, s); + + copy_cpu(l.outputs*l.batch, l.temp2_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.prev_cell_cpu, 1, l.temp_cpu, 1); + gradient_array(l.f_cpu, l.outputs*l.batch, LOGISTIC, l.temp_cpu); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, wf.delta, 1); + s.input = l.prev_state_cpu; + s.delta = l.dh_cpu; + backward_connected_layer(wf, s); + + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, uf.delta, 1); + s.input = state.input; + s.delta = state.delta; + backward_connected_layer(uf, s); + + copy_cpu(l.outputs*l.batch, l.temp2_cpu, 1, l.temp_cpu, 1); + mul_cpu(l.outputs*l.batch, l.f_cpu, 1, l.temp_cpu, 1); + copy_cpu(l.outputs*l.batch, l.temp_cpu, 1, l.dc_cpu, 1); + + state.input -= l.inputs*l.batch; + if (state.delta) state.delta -= l.inputs*l.batch; + l.output -= l.outputs*l.batch; + l.cell_cpu -= l.outputs*l.batch; + l.delta -= l.outputs*l.batch; + + increment_layer(&wf, -1); + increment_layer(&wi, -1); + increment_layer(&wg, -1); + increment_layer(&wo, -1); + + increment_layer(&uf, -1); + increment_layer(&ui, -1); + increment_layer(&ug, -1); + increment_layer(&uo, -1); + } +} + +#ifdef GPU +void update_lstm_layer_gpu(layer l, update_args a) +{ + update_connected_layer_gpu(*(l.wf), a); + update_connected_layer_gpu(*(l.wi), a); + update_connected_layer_gpu(*(l.wg), a); + update_connected_layer_gpu(*(l.wo), a); + update_connected_layer_gpu(*(l.uf), a); + update_connected_layer_gpu(*(l.ui), a); + update_connected_layer_gpu(*(l.ug), a); + update_connected_layer_gpu(*(l.uo), a); +} + +void forward_lstm_layer_gpu(layer l, network state) +{ + network s = { 0 }; + s.train = state.train; + int i; + layer wf = *(l.wf); + layer wi = *(l.wi); + layer wg = *(l.wg); + layer wo = *(l.wo); + + layer uf = *(l.uf); + layer ui = *(l.ui); + layer ug = *(l.ug); + layer uo = *(l.uo); + + fill_gpu(l.outputs * l.batch * l.steps, 0, wf.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wi.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wg.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, wo.delta_gpu, 1); + + fill_gpu(l.outputs * l.batch * l.steps, 0, uf.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, ui.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, ug.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, uo.delta_gpu, 1); + if (state.train) { + fill_gpu(l.outputs * l.batch * l.steps, 0, l.delta_gpu, 1); + } + + for (i = 0; i < l.steps; ++i) { + s.input_gpu = l.h_gpu; + forward_connected_layer_gpu(wf, s); + forward_connected_layer_gpu(wi, s); + forward_connected_layer_gpu(wg, s); + forward_connected_layer_gpu(wo, s); + + s.input_gpu = state.input_gpu; + forward_connected_layer_gpu(uf, s); + forward_connected_layer_gpu(ui, s); + forward_connected_layer_gpu(ug, s); + forward_connected_layer_gpu(uo, s); + + copy_gpu(l.outputs*l.batch, wf.output_gpu, 1, l.f_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, uf.output_gpu, 1, l.f_gpu, 1); + + copy_gpu(l.outputs*l.batch, wi.output_gpu, 1, l.i_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, ui.output_gpu, 1, l.i_gpu, 1); + + copy_gpu(l.outputs*l.batch, wg.output_gpu, 1, l.g_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, ug.output_gpu, 1, l.g_gpu, 1); + + copy_gpu(l.outputs*l.batch, wo.output_gpu, 1, l.o_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, uo.output_gpu, 1, l.o_gpu, 1); + + activate_array_gpu(l.f_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.i_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.g_gpu, l.outputs*l.batch, TANH); + activate_array_gpu(l.o_gpu, l.outputs*l.batch, LOGISTIC); + + copy_gpu(l.outputs*l.batch, l.i_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.g_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.f_gpu, 1, l.c_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, l.temp_gpu, 1, l.c_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.c_gpu, 1, l.h_gpu, 1); + activate_array_gpu(l.h_gpu, l.outputs*l.batch, TANH); + mul_gpu(l.outputs*l.batch, l.o_gpu, 1, l.h_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.c_gpu, 1, l.cell_gpu, 1); + copy_gpu(l.outputs*l.batch, l.h_gpu, 1, l.output_gpu, 1); + + state.input_gpu += l.inputs*l.batch; + l.output_gpu += l.outputs*l.batch; + l.cell_gpu += l.outputs*l.batch; + + increment_layer(&wf, 1); + increment_layer(&wi, 1); + increment_layer(&wg, 1); + increment_layer(&wo, 1); + + increment_layer(&uf, 1); + increment_layer(&ui, 1); + increment_layer(&ug, 1); + increment_layer(&uo, 1); + } +} + +void backward_lstm_layer_gpu(layer l, network state) +{ + network s = { 0 }; + s.train = state.train; + int i; + layer wf = *(l.wf); + layer wi = *(l.wi); + layer wg = *(l.wg); + layer wo = *(l.wo); + + layer uf = *(l.uf); + layer ui = *(l.ui); + layer ug = *(l.ug); + layer uo = *(l.uo); + + increment_layer(&wf, l.steps - 1); + increment_layer(&wi, l.steps - 1); + increment_layer(&wg, l.steps - 1); + increment_layer(&wo, l.steps - 1); + + increment_layer(&uf, l.steps - 1); + increment_layer(&ui, l.steps - 1); + increment_layer(&ug, l.steps - 1); + increment_layer(&uo, l.steps - 1); + + state.input_gpu += l.inputs*l.batch*(l.steps - 1); + if (state.delta_gpu) state.delta_gpu += l.inputs*l.batch*(l.steps - 1); + + l.output_gpu += l.outputs*l.batch*(l.steps - 1); + l.cell_gpu += l.outputs*l.batch*(l.steps - 1); + l.delta_gpu += l.outputs*l.batch*(l.steps - 1); + + for (i = l.steps - 1; i >= 0; --i) { + if (i != 0) copy_gpu(l.outputs*l.batch, l.cell_gpu - l.outputs*l.batch, 1, l.prev_cell_gpu, 1); + copy_gpu(l.outputs*l.batch, l.cell_gpu, 1, l.c_gpu, 1); + if (i != 0) copy_gpu(l.outputs*l.batch, l.output_gpu - l.outputs*l.batch, 1, l.prev_state_gpu, 1); + copy_gpu(l.outputs*l.batch, l.output_gpu, 1, l.h_gpu, 1); + + l.dh_gpu = (i == 0) ? 0 : l.delta_gpu - l.outputs*l.batch; + + copy_gpu(l.outputs*l.batch, wf.output_gpu, 1, l.f_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, uf.output_gpu, 1, l.f_gpu, 1); + + copy_gpu(l.outputs*l.batch, wi.output_gpu, 1, l.i_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, ui.output_gpu, 1, l.i_gpu, 1); + + copy_gpu(l.outputs*l.batch, wg.output_gpu, 1, l.g_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, ug.output_gpu, 1, l.g_gpu, 1); + + copy_gpu(l.outputs*l.batch, wo.output_gpu, 1, l.o_gpu, 1); + axpy_gpu(l.outputs*l.batch, 1, uo.output_gpu, 1, l.o_gpu, 1); + + activate_array_gpu(l.f_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.i_gpu, l.outputs*l.batch, LOGISTIC); + activate_array_gpu(l.g_gpu, l.outputs*l.batch, TANH); + activate_array_gpu(l.o_gpu, l.outputs*l.batch, LOGISTIC); + + copy_gpu(l.outputs*l.batch, l.delta_gpu, 1, l.temp3_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.c_gpu, 1, l.temp_gpu, 1); + activate_array_gpu(l.temp_gpu, l.outputs*l.batch, TANH); + + copy_gpu(l.outputs*l.batch, l.temp3_gpu, 1, l.temp2_gpu, 1); + mul_gpu(l.outputs*l.batch, l.o_gpu, 1, l.temp2_gpu, 1); + + gradient_array_gpu(l.temp_gpu, l.outputs*l.batch, TANH, l.temp2_gpu); + axpy_gpu(l.outputs*l.batch, 1, l.dc_gpu, 1, l.temp2_gpu, 1); + + copy_gpu(l.outputs*l.batch, l.c_gpu, 1, l.temp_gpu, 1); + activate_array_gpu(l.temp_gpu, l.outputs*l.batch, TANH); + mul_gpu(l.outputs*l.batch, l.temp3_gpu, 1, l.temp_gpu, 1); + gradient_array_gpu(l.o_gpu, l.outputs*l.batch, LOGISTIC, l.temp_gpu); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, wo.delta_gpu, 1); + s.input_gpu = l.prev_state_gpu; + s.delta_gpu = l.dh_gpu; + backward_connected_layer_gpu(wo, s); + + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, uo.delta_gpu, 1); + s.input_gpu = state.input_gpu; + s.delta_gpu = state.delta_gpu; + backward_connected_layer_gpu(uo, s); + + copy_gpu(l.outputs*l.batch, l.temp2_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.i_gpu, 1, l.temp_gpu, 1); + gradient_array_gpu(l.g_gpu, l.outputs*l.batch, TANH, l.temp_gpu); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, wg.delta_gpu, 1); + s.input_gpu = l.prev_state_gpu; + s.delta_gpu = l.dh_gpu; + backward_connected_layer_gpu(wg, s); + + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, ug.delta_gpu, 1); + s.input_gpu = state.input_gpu; + s.delta_gpu = state.delta_gpu; + backward_connected_layer_gpu(ug, s); + + copy_gpu(l.outputs*l.batch, l.temp2_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.g_gpu, 1, l.temp_gpu, 1); + gradient_array_gpu(l.i_gpu, l.outputs*l.batch, LOGISTIC, l.temp_gpu); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, wi.delta_gpu, 1); + s.input_gpu = l.prev_state_gpu; + s.delta_gpu = l.dh_gpu; + backward_connected_layer_gpu(wi, s); + + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, ui.delta_gpu, 1); + s.input_gpu = state.input_gpu; + s.delta_gpu = state.delta_gpu; + backward_connected_layer_gpu(ui, s); + + copy_gpu(l.outputs*l.batch, l.temp2_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.prev_cell_gpu, 1, l.temp_gpu, 1); + gradient_array_gpu(l.f_gpu, l.outputs*l.batch, LOGISTIC, l.temp_gpu); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, wf.delta_gpu, 1); + s.input_gpu = l.prev_state_gpu; + s.delta_gpu = l.dh_gpu; + backward_connected_layer_gpu(wf, s); + + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, uf.delta_gpu, 1); + s.input_gpu = state.input_gpu; + s.delta_gpu = state.delta_gpu; + backward_connected_layer_gpu(uf, s); + + copy_gpu(l.outputs*l.batch, l.temp2_gpu, 1, l.temp_gpu, 1); + mul_gpu(l.outputs*l.batch, l.f_gpu, 1, l.temp_gpu, 1); + copy_gpu(l.outputs*l.batch, l.temp_gpu, 1, l.dc_gpu, 1); + + state.input_gpu -= l.inputs*l.batch; + if (state.delta_gpu) state.delta_gpu -= l.inputs*l.batch; + l.output_gpu -= l.outputs*l.batch; + l.cell_gpu -= l.outputs*l.batch; + l.delta_gpu -= l.outputs*l.batch; + + increment_layer(&wf, -1); + increment_layer(&wi, -1); + increment_layer(&wg, -1); + increment_layer(&wo, -1); + + increment_layer(&uf, -1); + increment_layer(&ui, -1); + increment_layer(&ug, -1); + increment_layer(&uo, -1); + } +} +#endif diff --git a/image.darknet/inst/include/darknet/src/lstm_layer.h b/image.darknet/inst/include/darknet/src/lstm_layer.h new file mode 100644 index 0000000..b9f07e6 --- /dev/null +++ b/image.darknet/inst/include/darknet/src/lstm_layer.h @@ -0,0 +1,20 @@ +#ifndef LSTM_LAYER_H +#define LSTM_LAYER_H + +#include "activations.h" +#include "layer.h" +#include "network.h" +#define USET + +layer make_lstm_layer(int batch, int inputs, int outputs, int steps, int batch_normalize, int adam); + +void forward_lstm_layer(layer l, network net); +void update_lstm_layer(layer l, update_args a); + +#ifdef GPU +void forward_lstm_layer_gpu(layer l, network net); +void backward_lstm_layer_gpu(layer l, network net); +void update_lstm_layer_gpu(layer l, update_args a); + +#endif +#endif diff --git a/image.darknet/inst/include/darknet/src/matrix.c b/image.darknet/inst/include/darknet/src/matrix.c index ee14979..799916b 100644 --- a/image.darknet/inst/include/darknet/src/matrix.c +++ b/image.darknet/inst/include/darknet/src/matrix.c @@ -1,5 +1,6 @@ #include "matrix.h" #include "utils.h" +#include "blas.h" #include #include #include @@ -73,6 +74,20 @@ void matrix_add_matrix(matrix from, matrix to) } } +matrix copy_matrix(matrix m) +{ + matrix c = {0}; + c.rows = m.rows; + c.cols = m.cols; + c.vals = calloc(c.rows, sizeof(float *)); + int i; + for(i = 0; i < c.rows; ++i){ + c.vals[i] = calloc(c.cols, sizeof(float)); + copy_cpu(c.cols, m.vals[i], 1, c.vals[i], 1); + } + return c; +} + matrix make_matrix(int rows, int cols) { int i; diff --git a/image.darknet/inst/include/darknet/src/matrix.h b/image.darknet/inst/include/darknet/src/matrix.h index 641b596..879acd7 100644 --- a/image.darknet/inst/include/darknet/src/matrix.h +++ b/image.darknet/inst/include/darknet/src/matrix.h @@ -1,20 +1,11 @@ #ifndef MATRIX_H #define MATRIX_H -typedef struct matrix{ - int rows, cols; - float **vals; -} matrix; +#include "darknet.h" -matrix make_matrix(int rows, int cols); -void free_matrix(matrix m); +matrix copy_matrix(matrix m); void print_matrix(matrix m); -matrix csv_to_matrix(char *filename); -void matrix_to_csv(matrix m); matrix hold_out_matrix(matrix *m, int n); -float matrix_topk_accuracy(matrix truth, matrix guess, int k); -void matrix_add_matrix(matrix from, matrix to); -void scale_matrix(matrix m, float scale); matrix resize_matrix(matrix m, int size); float *pop_column(matrix *m, int c); diff --git a/image.darknet/inst/include/darknet/src/maxpool_layer.c b/image.darknet/inst/include/darknet/src/maxpool_layer.c index 031d116..fb05635 100644 --- a/image.darknet/inst/include/darknet/src/maxpool_layer.c +++ b/image.darknet/inst/include/darknet/src/maxpool_layer.c @@ -27,8 +27,8 @@ maxpool_layer make_maxpool_layer(int batch, int h, int w, int c, int size, int s l.w = w; l.c = c; l.pad = padding; - l.out_w = (w + 2*padding)/stride; - l.out_h = (h + 2*padding)/stride; + l.out_w = (w + padding - size)/stride + 1; + l.out_h = (h + padding - size)/stride + 1; l.out_c = c; l.outputs = l.out_h * l.out_w * l.out_c; l.inputs = h*w*c; @@ -43,7 +43,7 @@ maxpool_layer make_maxpool_layer(int batch, int h, int w, int c, int size, int s #ifdef GPU l.forward_gpu = forward_maxpool_layer_gpu; l.backward_gpu = backward_maxpool_layer_gpu; - l.indexes_gpu = cuda_make_int_array(output_size); + l.indexes_gpu = cuda_make_int_array(0, output_size); l.output_gpu = cuda_make_array(l.output, output_size); l.delta_gpu = cuda_make_array(l.delta, output_size); #endif @@ -57,8 +57,8 @@ void resize_maxpool_layer(maxpool_layer *l, int w, int h) l->w = w; l->inputs = h*w*l->c; - l->out_w = (w + 2*l->pad)/l->stride; - l->out_h = (h + 2*l->pad)/l->stride; + l->out_w = (w + l->pad - l->size)/l->stride + 1; + l->out_h = (h + l->pad - l->size)/l->stride + 1; l->outputs = l->out_w * l->out_h * l->c; int output_size = l->outputs * l->batch; @@ -70,17 +70,17 @@ void resize_maxpool_layer(maxpool_layer *l, int w, int h) cuda_free((float *)l->indexes_gpu); cuda_free(l->output_gpu); cuda_free(l->delta_gpu); - l->indexes_gpu = cuda_make_int_array(output_size); + l->indexes_gpu = cuda_make_int_array(0, output_size); l->output_gpu = cuda_make_array(l->output, output_size); l->delta_gpu = cuda_make_array(l->delta, output_size); #endif } -void forward_maxpool_layer(const maxpool_layer l, network_state state) +void forward_maxpool_layer(const maxpool_layer l, network net) { int b,i,j,k,m,n; - int w_offset = -l.pad; - int h_offset = -l.pad; + int w_offset = -l.pad/2; + int h_offset = -l.pad/2; int h = l.out_h; int w = l.out_w; @@ -100,7 +100,7 @@ void forward_maxpool_layer(const maxpool_layer l, network_state state) int index = cur_w + l.w*(cur_h + l.h*(k + b*l.c)); int valid = (cur_h >= 0 && cur_h < l.h && cur_w >= 0 && cur_w < l.w); - float val = (valid != 0) ? state.input[index] : -FLT_MAX; + float val = (valid != 0) ? net.input[index] : -FLT_MAX; max_i = (val > max) ? index : max_i; max = (val > max) ? val : max; } @@ -113,7 +113,7 @@ void forward_maxpool_layer(const maxpool_layer l, network_state state) } } -void backward_maxpool_layer(const maxpool_layer l, network_state state) +void backward_maxpool_layer(const maxpool_layer l, network net) { int i; int h = l.out_h; @@ -121,7 +121,7 @@ void backward_maxpool_layer(const maxpool_layer l, network_state state) int c = l.c; for(i = 0; i < h*w*c*l.batch; ++i){ int index = l.indexes[i]; - state.delta[index] += l.delta[i]; + net.delta[index] += l.delta[i]; } } diff --git a/image.darknet/inst/include/darknet/src/maxpool_layer.h b/image.darknet/inst/include/darknet/src/maxpool_layer.h index ce56dd8..ceb5190 100644 --- a/image.darknet/inst/include/darknet/src/maxpool_layer.h +++ b/image.darknet/inst/include/darknet/src/maxpool_layer.h @@ -11,12 +11,12 @@ typedef layer maxpool_layer; image get_maxpool_image(maxpool_layer l); maxpool_layer make_maxpool_layer(int batch, int h, int w, int c, int size, int stride, int padding); void resize_maxpool_layer(maxpool_layer *l, int w, int h); -void forward_maxpool_layer(const maxpool_layer l, network_state state); -void backward_maxpool_layer(const maxpool_layer l, network_state state); +void forward_maxpool_layer(const maxpool_layer l, network net); +void backward_maxpool_layer(const maxpool_layer l, network net); #ifdef GPU -void forward_maxpool_layer_gpu(maxpool_layer l, network_state state); -void backward_maxpool_layer_gpu(maxpool_layer l, network_state state); +void forward_maxpool_layer_gpu(maxpool_layer l, network net); +void backward_maxpool_layer_gpu(maxpool_layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/maxpool_layer_kernels.cu b/image.darknet/inst/include/darknet/src/maxpool_layer_kernels.cu index 6381cc1..869ef46 100644 --- a/image.darknet/inst/include/darknet/src/maxpool_layer_kernels.cu +++ b/image.darknet/inst/include/darknet/src/maxpool_layer_kernels.cu @@ -9,8 +9,8 @@ extern "C" { __global__ void forward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_c, int stride, int size, int pad, float *input, float *output, int *indexes) { - int h = (in_h + 2*pad)/stride; - int w = (in_w + 2*pad)/stride; + int h = (in_h + pad - size)/stride + 1; + int w = (in_w + pad - size)/stride + 1; int c = in_c; int id = (blockIdx.x + blockIdx.y*gridDim.x) * blockDim.x + threadIdx.x; @@ -24,8 +24,8 @@ __global__ void forward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_c id /= c; int b = id; - int w_offset = -pad; - int h_offset = -pad; + int w_offset = -pad/2; + int h_offset = -pad/2; int out_index = j + w*(i + h*(k + c*b)); float max = -INFINITY; @@ -49,8 +49,8 @@ __global__ void forward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_c __global__ void backward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_c, int stride, int size, int pad, float *delta, float *prev_delta, int *indexes) { - int h = (in_h + 2*pad)/stride; - int w = (in_w + 2*pad)/stride; + int h = (in_h + pad - size)/stride + 1; + int w = (in_w + pad - size)/stride + 1; int c = in_c; int area = (size-1)/stride; @@ -66,8 +66,8 @@ __global__ void backward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_ id /= in_c; int b = id; - int w_offset = -pad; - int h_offset = -pad; + int w_offset = -pad/2; + int h_offset = -pad/2; float d = 0; int l, m; @@ -84,7 +84,7 @@ __global__ void backward_maxpool_layer_kernel(int n, int in_h, int in_w, int in_ prev_delta[index] += d; } -extern "C" void forward_maxpool_layer_gpu(maxpool_layer layer, network_state state) +extern "C" void forward_maxpool_layer_gpu(maxpool_layer layer, network net) { int h = layer.out_h; int w = layer.out_w; @@ -92,15 +92,15 @@ extern "C" void forward_maxpool_layer_gpu(maxpool_layer layer, network_state sta size_t n = h*w*c*layer.batch; - forward_maxpool_layer_kernel<<>>(n, layer.h, layer.w, layer.c, layer.stride, layer.size, layer.pad, state.input, layer.output_gpu, layer.indexes_gpu); + forward_maxpool_layer_kernel<<>>(n, layer.h, layer.w, layer.c, layer.stride, layer.size, layer.pad, net.input_gpu, layer.output_gpu, layer.indexes_gpu); check_error(cudaPeekAtLastError()); } -extern "C" void backward_maxpool_layer_gpu(maxpool_layer layer, network_state state) +extern "C" void backward_maxpool_layer_gpu(maxpool_layer layer, network net) { size_t n = layer.h*layer.w*layer.c*layer.batch; - backward_maxpool_layer_kernel<<>>(n, layer.h, layer.w, layer.c, layer.stride, layer.size, layer.pad, layer.delta_gpu, state.delta, layer.indexes_gpu); + backward_maxpool_layer_kernel<<>>(n, layer.h, layer.w, layer.c, layer.stride, layer.size, layer.pad, layer.delta_gpu, net.delta_gpu, layer.indexes_gpu); check_error(cudaPeekAtLastError()); } diff --git a/image.darknet/inst/include/darknet/src/network.c b/image.darknet/inst/include/darknet/src/network.c index 0914e37..aaab799 100644 --- a/image.darknet/inst/include/darknet/src/network.c +++ b/image.darknet/inst/include/darknet/src/network.c @@ -17,6 +17,7 @@ #include "activation_layer.h" #include "detection_layer.h" #include "region_layer.h" +#include "yolo_layer.h" #include "normalization_layer.h" #include "batchnorm_layer.h" #include "maxpool_layer.h" @@ -26,55 +27,95 @@ #include "softmax_layer.h" #include "dropout_layer.h" #include "route_layer.h" +#include "upsample_layer.h" #include "shortcut_layer.h" +#include "parser.h" +#include "data.h" + +load_args get_base_args(network *net) +{ + load_args args = {0}; + args.w = net->w; + args.h = net->h; + args.size = net->w; + + args.min = net->min_crop; + args.max = net->max_crop; + args.angle = net->angle; + args.aspect = net->aspect; + args.exposure = net->exposure; + args.center = net->center; + args.saturation = net->saturation; + args.hue = net->hue; + return args; +} + +network *load_network(char *cfg, char *weights, int clear) +{ + network *net = parse_network_cfg(cfg); + if(weights && weights[0] != 0){ + load_weights(net, weights); + } + if(clear) (*net->seen) = 0; + return net; +} -int get_current_batch(network net) +size_t get_current_batch(network *net) { - int batch_num = (*net.seen)/(net.batch*net.subdivisions); + size_t batch_num = (*net->seen)/(net->batch*net->subdivisions); return batch_num; } -void reset_momentum(network net) +void reset_network_state(network *net, int b) { - if (net.momentum == 0) return; - net.learning_rate = 0; - net.momentum = 0; - net.decay = 0; - #ifdef GPU - //if(net.gpu_index >= 0) update_network_gpu(net); - #endif + int i; + for (i = 0; i < net->n; ++i) { + #ifdef GPU + layer l = net->layers[i]; + if(l.state_gpu){ + fill_gpu(l.outputs, 0, l.state_gpu + l.outputs*b, 1); + } + if(l.h_gpu){ + fill_gpu(l.outputs, 0, l.h_gpu + l.outputs*b, 1); + } + #endif + } } -float get_current_rate(network net) +void reset_rnn(network *net) { - int batch_num = get_current_batch(net); + reset_network_state(net, 0); +} + +float get_current_rate(network *net) +{ + size_t batch_num = get_current_batch(net); int i; float rate; - switch (net.policy) { + if (batch_num < net->burn_in) return net->learning_rate * pow((float)batch_num / net->burn_in, net->power); + switch (net->policy) { case CONSTANT: - return net.learning_rate; + return net->learning_rate; case STEP: - return net.learning_rate * pow(net.scale, batch_num/net.step); + return net->learning_rate * pow(net->scale, batch_num/net->step); case STEPS: - rate = net.learning_rate; - for(i = 0; i < net.num_steps; ++i){ - if(net.steps[i] > batch_num) return rate; - rate *= net.scales[i]; - //if(net.steps[i] > batch_num - 1 && net.scales[i] > 1) reset_momentum(net); + rate = net->learning_rate; + for(i = 0; i < net->num_steps; ++i){ + if(net->steps[i] > batch_num) return rate; + rate *= net->scales[i]; } return rate; case EXP: - return net.learning_rate * pow(net.gamma, batch_num); + return net->learning_rate * pow(net->gamma, batch_num); case POLY: - if (batch_num < net.burn_in) return net.learning_rate * pow((float)batch_num / net.burn_in, net.power); - return net.learning_rate * pow(1 - (float)batch_num / net.max_batches, net.power); + return net->learning_rate * pow(1 - (float)batch_num / net->max_batches, net->power); case RANDOM: - return net.learning_rate * pow(rand_uniform(0,1), net.power); + return net->learning_rate * pow(rand_uniform(0,1), net->power); case SIG: - return net.learning_rate * (1./(1.+exp(net.gamma*(batch_num - net.step)))); + return net->learning_rate * (1./(1.+exp(net->gamma*(batch_num - net->step)))); default: fprintf(stderr, "Policy is weird!\n"); - return net.learning_rate; + return net->learning_rate; } } @@ -95,6 +136,8 @@ char *get_layer_string(LAYER_TYPE a) return "rnn"; case GRU: return "gru"; + case LSTM: + return "lstm"; case CRNN: return "crnn"; case MAXPOOL: @@ -109,6 +152,8 @@ char *get_layer_string(LAYER_TYPE a) return "detection"; case REGION: return "region"; + case YOLO: + return "yolo"; case DROPOUT: return "dropout"; case CROP: @@ -129,59 +174,75 @@ char *get_layer_string(LAYER_TYPE a) return "none"; } -network make_network(int n) +network *make_network(int n) { - network net = {0}; - net.n = n; - net.layers = calloc(net.n, sizeof(layer)); - net.seen = calloc(1, sizeof(int)); - #ifdef GPU - net.input_gpu = calloc(1, sizeof(float *)); - net.truth_gpu = calloc(1, sizeof(float *)); - #endif + network *net = calloc(1, sizeof(network)); + net->n = n; + net->layers = calloc(net->n, sizeof(layer)); + net->seen = calloc(1, sizeof(size_t)); + net->t = calloc(1, sizeof(int)); + net->cost = calloc(1, sizeof(float)); return net; } -void forward_network(network net, network_state state) +void forward_network(network *netp) { - state.workspace = net.workspace; +#ifdef GPU + if(netp->gpu_index >= 0){ + forward_network_gpu(netp); + return; + } +#endif + network net = *netp; int i; for(i = 0; i < net.n; ++i){ - state.index = i; + net.index = i; layer l = net.layers[i]; if(l.delta){ - scal_cpu(l.outputs * l.batch, 0, l.delta, 1); + fill_cpu(l.outputs * l.batch, 0, l.delta, 1); + } + l.forward(l, net); + net.input = l.output; + if(l.truth) { + net.truth = l.output; } - l.forward(l, state); - state.input = l.output; } + calc_network_cost(netp); } -void update_network(network net) +void update_network(network *netp) { +#ifdef GPU + if(netp->gpu_index >= 0){ + update_network_gpu(netp); + return; + } +#endif + network net = *netp; int i; - int update_batch = net.batch*net.subdivisions; - float rate = get_current_rate(net); + update_args a = {0}; + a.batch = net.batch*net.subdivisions; + a.learning_rate = get_current_rate(netp); + a.momentum = net.momentum; + a.decay = net.decay; + a.adam = net.adam; + a.B1 = net.B1; + a.B2 = net.B2; + a.eps = net.eps; + ++*net.t; + a.t = *net.t; + for(i = 0; i < net.n; ++i){ layer l = net.layers[i]; if(l.update){ - l.update(l, update_batch, rate, net.momentum, net.decay); + l.update(l, a); } } } -float *get_network_output(network net) -{ -#ifdef GPU - if (gpu_index >= 0) return get_network_output_gpu(net); -#endif - int i; - for(i = net.n-1; i > 0; --i) if(net.layers[i].type != COST) break; - return net.layers[i].output; -} - -float get_network_cost(network net) +void calc_network_cost(network *netp) { + network net = *netp; int i; float sum = 0; int count = 0; @@ -191,120 +252,90 @@ float get_network_cost(network net) ++count; } } - return sum/count; + *net.cost = sum/count; } -int get_predicted_class_network(network net) +int get_predicted_class_network(network *net) { - float *out = get_network_output(net); - int k = get_network_output_size(net); - return max_index(out, k); + return max_index(net->output, net->outputs); } -void backward_network(network net, network_state state) +void backward_network(network *netp) { +#ifdef GPU + if(netp->gpu_index >= 0){ + backward_network_gpu(netp); + return; + } +#endif + network net = *netp; int i; - float *original_input = state.input; - float *original_delta = state.delta; - state.workspace = net.workspace; + network orig = net; for(i = net.n-1; i >= 0; --i){ - state.index = i; + layer l = net.layers[i]; + if(l.stopbackward) break; if(i == 0){ - state.input = original_input; - state.delta = original_delta; + net = orig; }else{ layer prev = net.layers[i-1]; - state.input = prev.output; - state.delta = prev.delta; + net.input = prev.output; + net.delta = prev.delta; } - layer l = net.layers[i]; - l.backward(l, state); + net.index = i; + l.backward(l, net); } } -float train_network_datum(network net, float *x, float *y) +float train_network_datum(network *net) { -#ifdef GPU - if(gpu_index >= 0) return train_network_datum_gpu(net, x, y); -#endif - network_state state; - *net.seen += net.batch; - state.index = 0; - state.net = net; - state.input = x; - state.delta = 0; - state.truth = y; - state.train = 1; - forward_network(net, state); - backward_network(net, state); - float error = get_network_cost(net); - if(((*net.seen)/net.batch)%net.subdivisions == 0) update_network(net); + *net->seen += net->batch; + net->train = 1; + forward_network(net); + backward_network(net); + float error = *net->cost; + if(((*net->seen)/net->batch)%net->subdivisions == 0) update_network(net); return error; } -float train_network_sgd(network net, data d, int n) +float train_network_sgd(network *net, data d, int n) { - int batch = net.batch; - float *X = calloc(batch*d.X.cols, sizeof(float)); - float *y = calloc(batch*d.y.cols, sizeof(float)); + int batch = net->batch; int i; float sum = 0; for(i = 0; i < n; ++i){ - get_random_batch(d, batch, X, y); - float err = train_network_datum(net, X, y); + get_random_batch(d, batch, net->input, net->truth); + float err = train_network_datum(net); sum += err; } - free(X); - free(y); return (float)sum/(n*batch); } -float train_network(network net, data d) +float train_network(network *net, data d) { - assert(d.X.rows % net.batch == 0); - int batch = net.batch; + assert(d.X.rows % net->batch == 0); + int batch = net->batch; int n = d.X.rows / batch; - float *X = calloc(batch*d.X.cols, sizeof(float)); - float *y = calloc(batch*d.y.cols, sizeof(float)); int i; float sum = 0; for(i = 0; i < n; ++i){ - get_next_batch(d, batch, i*batch, X, y); - float err = train_network_datum(net, X, y); + get_next_batch(d, batch, i*batch, net->input, net->truth); + float err = train_network_datum(net); sum += err; } - free(X); - free(y); return (float)sum/(n*batch); } - -float train_network_batch(network net, data d, int n) +void set_temp_network(network *net, float t) { - int i,j; - network_state state; - state.index = 0; - state.net = net; - state.train = 1; - state.delta = 0; - float sum = 0; - int batch = 2; - for(i = 0; i < n; ++i){ - for(j = 0; j < batch; ++j){ - int index = rand()%d.X.rows; - state.input = d.X.vals[index]; - state.truth = d.y.vals[index]; - forward_network(net, state); - backward_network(net, state); - sum += get_network_cost(net); - } - update_network(net); + int i; + for(i = 0; i < net->n; ++i){ + net->layers[i].temperature = t; } - return (float)sum/(n*batch); } + void set_batch_network(network *net, int b) { net->batch = b; @@ -315,6 +346,11 @@ void set_batch_network(network *net, int b) if(net->layers[i].type == CONVOLUTIONAL){ cudnn_convolutional_setup(net->layers + i); } + if(net->layers[i].type == DECONVOLUTIONAL){ + layer *l = net->layers + i; + cudnnSetTensor4dDescriptor(l->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l->out_c, l->out_h, l->out_w); + cudnnSetTensor4dDescriptor(l->normTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, l->out_c, 1, 1); + } #endif } } @@ -323,9 +359,7 @@ int resize_network(network *net, int w, int h) { #ifdef GPU cuda_set_device(net->gpu_index); - if(gpu_index >= 0){ - cuda_free(net->workspace); - } + cuda_free(net->workspace); #endif int i; //if(w == net->w && h == net->h) return 0; @@ -345,8 +379,14 @@ int resize_network(network *net, int w, int h) resize_maxpool_layer(&l, w, h); }else if(l.type == REGION){ resize_region_layer(&l, w, h); + }else if(l.type == YOLO){ + resize_yolo_layer(&l, w, h); }else if(l.type == ROUTE){ resize_route_layer(&l, net); + }else if(l.type == SHORTCUT){ + resize_shortcut_layer(&l, w, h); + }else if(l.type == UPSAMPLE){ + resize_upsample_layer(&l, w, h); }else if(l.type == REORG){ resize_reorg_layer(&l, w, h); }else if(l.type == AVGPOOL){ @@ -359,21 +399,32 @@ int resize_network(network *net, int w, int h) error("Cannot resize this type of layer"); } if(l.workspace_size > workspace_size) workspace_size = l.workspace_size; + if(l.workspace_size > 2000000000) assert(0); inputs = l.outputs; net->layers[i] = l; w = l.out_w; h = l.out_h; if(l.type == AVGPOOL) break; } + layer out = get_network_output_layer(net); + net->inputs = net->layers[0].inputs; + net->outputs = out.outputs; + net->truths = out.outputs; + if(net->layers[net->n-1].truths) net->truths = net->layers[net->n-1].truths; + net->output = out.output; + free(net->input); + free(net->truth); + net->input = calloc(net->inputs*net->batch, sizeof(float)); + net->truth = calloc(net->truths*net->batch, sizeof(float)); #ifdef GPU if(gpu_index >= 0){ - if(net->input_gpu) { - cuda_free(*net->input_gpu); - *net->input_gpu = 0; - cuda_free(*net->truth_gpu); - *net->truth_gpu = 0; + cuda_free(net->input_gpu); + cuda_free(net->truth_gpu); + net->input_gpu = cuda_make_array(net->input, net->inputs*net->batch); + net->truth_gpu = cuda_make_array(net->truth, net->truths*net->batch); + if(workspace_size){ + net->workspace = cuda_make_array(0, (workspace_size-1)/sizeof(float)+1); } - net->workspace = cuda_make_array(0, (workspace_size-1)/sizeof(float)+1); }else { free(net->workspace); net->workspace = calloc(1, workspace_size); @@ -386,34 +437,25 @@ int resize_network(network *net, int w, int h) return 0; } -int get_network_output_size(network net) +layer get_network_detection_layer(network *net) { int i; - for(i = net.n-1; i > 0; --i) if(net.layers[i].type != COST) break; - return net.layers[i].outputs; -} - -int get_network_input_size(network net) -{ - return net.layers[0].inputs; -} - -detection_layer get_network_detection_layer(network net) -{ - int i; - for(i = 0; i < net.n; ++i){ - if(net.layers[i].type == DETECTION){ - return net.layers[i]; + for(i = 0; i < net->n; ++i){ + if(net->layers[i].type == DETECTION){ + return net->layers[i]; } } fprintf(stderr, "Detection layer not found!!\n"); - detection_layer l = {0}; + layer l = {0}; return l; } -image get_network_image_layer(network net, int i) +image get_network_image_layer(network *net, int i) { - layer l = net.layers[i]; + layer l = net->layers[i]; +#ifdef GPU + //cuda_pull_array(l.output_gpu, l.output, l.outputs); +#endif if (l.out_w && l.out_h && l.out_c){ return float_to_image(l.out_w, l.out_h, l.out_c, l.output); } @@ -421,10 +463,10 @@ image get_network_image_layer(network net, int i) return def; } -image get_network_image(network net) +image get_network_image(network *net) { int i; - for(i = net.n-1; i >= 0; --i){ + for(i = net->n-1; i >= 0; --i){ image m = get_network_image_layer(net, i); if(m.h != 0) return m; } @@ -432,60 +474,134 @@ image get_network_image(network net) return def; } -void visualize_network(network net) +void visualize_network(network *net) { image *prev = 0; int i; char buff[256]; - for(i = 0; i < net.n; ++i){ + for(i = 0; i < net->n; ++i){ sprintf(buff, "Layer %d", i); - layer l = net.layers[i]; + layer l = net->layers[i]; if(l.type == CONVOLUTIONAL){ prev = visualize_convolutional_layer(l, buff, prev); } } } -void top_predictions(network net, int k, int *index) +void top_predictions(network *net, int k, int *index) { - int size = get_network_output_size(net); - float *out = get_network_output(net); - top_k(out, size, k, index); + top_k(net->output, net->outputs, k, index); } -float *network_predict(network net, float *input) +float *network_predict(network *net, float *input) { -#ifdef GPU - if(gpu_index >= 0) return network_predict_gpu(net, input); -#endif - - network_state state; - state.net = net; - state.index = 0; - state.input = input; - state.truth = 0; - state.train = 0; - state.delta = 0; - forward_network(net, state); - float *out = get_network_output(net); + network orig = *net; + net->input = input; + net->truth = 0; + net->train = 0; + net->delta = 0; + forward_network(net); + float *out = net->output; + *net = orig; return out; } -matrix network_predict_data_multi(network net, data test, int n) +int num_detections(network *net, float thresh) +{ + int i; + int s = 0; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; + if(l.type == YOLO){ + s += yolo_num_detections(l, thresh); + } + if(l.type == DETECTION || l.type == REGION){ + s += l.w*l.h*l.n; + } + } + return s; +} + +detection *make_network_boxes(network *net, float thresh, int *num) +{ + layer l = net->layers[net->n - 1]; + int i; + int nboxes = num_detections(net, thresh); + if(num) *num = nboxes; + detection *dets = calloc(nboxes, sizeof(detection)); + for(i = 0; i < nboxes; ++i){ + dets[i].prob = calloc(l.classes, sizeof(float)); + if(l.coords > 4){ + dets[i].mask = calloc(l.coords-4, sizeof(float)); + } + } + return dets; +} + +void fill_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, detection *dets) +{ + int j; + for(j = 0; j < net->n; ++j){ + layer l = net->layers[j]; + if(l.type == YOLO){ + int count = get_yolo_detections(l, w, h, net->w, net->h, thresh, map, relative, dets); + dets += count; + } + if(l.type == REGION){ + get_region_detections(l, w, h, net->w, net->h, thresh, map, hier, relative, dets); + dets += l.w*l.h*l.n; + } + if(l.type == DETECTION){ + get_detection_detections(l, w, h, thresh, dets); + dets += l.w*l.h*l.n; + } + } +} + +detection *get_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, int *num) +{ + detection *dets = make_network_boxes(net, thresh, num); + fill_network_boxes(net, w, h, thresh, hier, map, relative, dets); + return dets; +} + +void free_detections(detection *dets, int n) +{ + int i; + for(i = 0; i < n; ++i){ + free(dets[i].prob); + if(dets[i].mask) free(dets[i].mask); + } + free(dets); +} + +float *network_predict_image(network *net, image im) +{ + image imr = letterbox_image(im, net->w, net->h); + set_batch_network(net, 1); + float *p = network_predict(net, imr.data); + free_image(imr); + return p; +} + +int network_width(network *net){return net->w;} +int network_height(network *net){return net->h;} + +matrix network_predict_data_multi(network *net, data test, int n) { int i,j,b,m; - int k = get_network_output_size(net); + int k = net->outputs; matrix pred = make_matrix(test.X.rows, k); - float *X = calloc(net.batch*test.X.rows, sizeof(float)); - for(i = 0; i < test.X.rows; i += net.batch){ - for(b = 0; b < net.batch; ++b){ + float *X = calloc(net->batch*test.X.rows, sizeof(float)); + for(i = 0; i < test.X.rows; i += net->batch){ + for(b = 0; b < net->batch; ++b){ if(i+b == test.X.rows) break; memcpy(X+b*test.X.cols, test.X.vals[i+b], test.X.cols*sizeof(float)); } for(m = 0; m < n; ++m){ float *out = network_predict(net, X); - for(b = 0; b < net.batch; ++b){ + for(b = 0; b < net->batch; ++b){ if(i+b == test.X.rows) break; for(j = 0; j < k; ++j){ pred.vals[i+b][j] += out[j+b*k]/n; @@ -497,19 +613,19 @@ matrix network_predict_data_multi(network net, data test, int n) return pred; } -matrix network_predict_data(network net, data test) +matrix network_predict_data(network *net, data test) { int i,j,b; - int k = get_network_output_size(net); + int k = net->outputs; matrix pred = make_matrix(test.X.rows, k); - float *X = calloc(net.batch*test.X.cols, sizeof(float)); - for(i = 0; i < test.X.rows; i += net.batch){ - for(b = 0; b < net.batch; ++b){ + float *X = calloc(net->batch*test.X.cols, sizeof(float)); + for(i = 0; i < test.X.rows; i += net->batch){ + for(b = 0; b < net->batch; ++b){ if(i+b == test.X.rows) break; memcpy(X+b*test.X.cols, test.X.vals[i+b], test.X.cols*sizeof(float)); } float *out = network_predict(net, X); - for(b = 0; b < net.batch; ++b){ + for(b = 0; b < net->batch; ++b){ if(i+b == test.X.rows) break; for(j = 0; j < k; ++j){ pred.vals[i+b][j] = out[j+b*k]; @@ -520,11 +636,11 @@ matrix network_predict_data(network net, data test) return pred; } -void print_network(network net) +void print_network(network *net) { int i,j; - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; + for(i = 0; i < net->n; ++i){ + layer l = net->layers[i]; float *output = l.output; int n = l.outputs; float mean = mean_array(output, n); @@ -537,7 +653,7 @@ void print_network(network net) } } -void compare_networks(network n1, network n2, data test) +void compare_networks(network *n1, network *n2, data test) { matrix g1 = network_predict_data(n1, test); matrix g2 = network_predict_data(n2, test); @@ -562,7 +678,7 @@ void compare_networks(network n1, network n2, data test) printf("%f\n", num/den); } -float network_accuracy(network net, data d) +float network_accuracy(network *net, data d) { matrix guess = network_predict_data(net, d); float acc = matrix_topk_accuracy(d.y, guess,1); @@ -570,7 +686,7 @@ float network_accuracy(network net, data d) return acc; } -float *network_accuracies(network net, data d, int n) +float *network_accuracies(network *net, data d, int n) { static float acc[2]; matrix guess = network_predict_data(net, d); @@ -580,7 +696,16 @@ float *network_accuracies(network net, data d, int n) return acc; } -float network_accuracy_multi(network net, data d, int n) +layer get_network_output_layer(network *net) +{ + int i; + for(i = net->n - 1; i >= 0; --i){ + if(net->layers[i].type != COST) break; + } + return net->layers[i]; +} + +float network_accuracy_multi(network *net, data d, int n) { matrix guess = network_predict_data_multi(net, d, n); float acc = matrix_topk_accuracy(d.y, guess,1); @@ -588,17 +713,417 @@ float network_accuracy_multi(network net, data d, int n) return acc; } -void free_network(network net) +void free_network(network *net) { int i; - for(i = 0; i < net.n; ++i){ - free_layer(net.layers[i]); + for(i = 0; i < net->n; ++i){ + free_layer(net->layers[i]); } - free(net.layers); + free(net->layers); + if(net->input) free(net->input); + if(net->truth) free(net->truth); #ifdef GPU - if(*net.input_gpu) cuda_free(*net.input_gpu); - if(*net.truth_gpu) cuda_free(*net.truth_gpu); - if(net.input_gpu) free(net.input_gpu); - if(net.truth_gpu) free(net.truth_gpu); + if(net->input_gpu) cuda_free(net->input_gpu); + if(net->truth_gpu) cuda_free(net->truth_gpu); #endif + free(net); +} + +// Some day... +// ^ What the hell is this comment for? + + +layer network_output_layer(network *net) +{ + int i; + for(i = net->n - 1; i >= 0; --i){ + if(net->layers[i].type != COST) break; + } + return net->layers[i]; } + +int network_inputs(network *net) +{ + return net->layers[0].inputs; +} + +int network_outputs(network *net) +{ + return network_output_layer(net).outputs; +} + +float *network_output(network *net) +{ + return network_output_layer(net).output; +} + +#ifdef GPU + +void forward_network_gpu(network *netp) +{ + network net = *netp; + cuda_set_device(net.gpu_index); + cuda_push_array(net.input_gpu, net.input, net.inputs*net.batch); + if(net.truth){ + cuda_push_array(net.truth_gpu, net.truth, net.truths*net.batch); + } + + int i; + for(i = 0; i < net.n; ++i){ + net.index = i; + layer l = net.layers[i]; + if(l.delta_gpu){ + fill_gpu(l.outputs * l.batch, 0, l.delta_gpu, 1); + } + l.forward_gpu(l, net); + net.input_gpu = l.output_gpu; + net.input = l.output; + if(l.truth) { + net.truth_gpu = l.output_gpu; + net.truth = l.output; + } + } + pull_network_output(netp); + calc_network_cost(netp); +} + +void backward_network_gpu(network *netp) +{ + int i; + network net = *netp; + network orig = net; + cuda_set_device(net.gpu_index); + for(i = net.n-1; i >= 0; --i){ + layer l = net.layers[i]; + if(l.stopbackward) break; + if(i == 0){ + net = orig; + }else{ + layer prev = net.layers[i-1]; + net.input = prev.output; + net.delta = prev.delta; + net.input_gpu = prev.output_gpu; + net.delta_gpu = prev.delta_gpu; + } + net.index = i; + l.backward_gpu(l, net); + } +} + +void update_network_gpu(network *netp) +{ + network net = *netp; + cuda_set_device(net.gpu_index); + int i; + update_args a = {0}; + a.batch = net.batch*net.subdivisions; + a.learning_rate = get_current_rate(netp); + a.momentum = net.momentum; + a.decay = net.decay; + a.adam = net.adam; + a.B1 = net.B1; + a.B2 = net.B2; + a.eps = net.eps; + ++*net.t; + a.t = (*net.t); + + for(i = 0; i < net.n; ++i){ + layer l = net.layers[i]; + if(l.update_gpu){ + l.update_gpu(l, a); + } + } +} + +void harmless_update_network_gpu(network *netp) +{ + network net = *netp; + cuda_set_device(net.gpu_index); + int i; + for(i = 0; i < net.n; ++i){ + layer l = net.layers[i]; + if(l.weight_updates_gpu) fill_gpu(l.nweights, 0, l.weight_updates_gpu, 1); + if(l.bias_updates_gpu) fill_gpu(l.nbiases, 0, l.bias_updates_gpu, 1); + if(l.scale_updates_gpu) fill_gpu(l.nbiases, 0, l.scale_updates_gpu, 1); + } +} + +typedef struct { + network *net; + data d; + float *err; +} train_args; + +void *train_thread(void *ptr) +{ + train_args args = *(train_args*)ptr; + free(ptr); + cuda_set_device(args.net->gpu_index); + *args.err = train_network(args.net, args.d); + return 0; +} + +pthread_t train_network_in_thread(network *net, data d, float *err) +{ + pthread_t thread; + train_args *ptr = (train_args *)calloc(1, sizeof(train_args)); + ptr->net = net; + ptr->d = d; + ptr->err = err; + if(pthread_create(&thread, 0, train_thread, ptr)) error("Thread creation failed"); + return thread; +} + +void merge_weights(layer l, layer base) +{ + if (l.type == CONVOLUTIONAL) { + axpy_cpu(l.n, 1, l.bias_updates, 1, base.biases, 1); + axpy_cpu(l.nweights, 1, l.weight_updates, 1, base.weights, 1); + if (l.scales) { + axpy_cpu(l.n, 1, l.scale_updates, 1, base.scales, 1); + } + } else if(l.type == CONNECTED) { + axpy_cpu(l.outputs, 1, l.bias_updates, 1, base.biases, 1); + axpy_cpu(l.outputs*l.inputs, 1, l.weight_updates, 1, base.weights, 1); + } +} + +void scale_weights(layer l, float s) +{ + if (l.type == CONVOLUTIONAL) { + scal_cpu(l.n, s, l.biases, 1); + scal_cpu(l.nweights, s, l.weights, 1); + if (l.scales) { + scal_cpu(l.n, s, l.scales, 1); + } + } else if(l.type == CONNECTED) { + scal_cpu(l.outputs, s, l.biases, 1); + scal_cpu(l.outputs*l.inputs, s, l.weights, 1); + } +} + + +void pull_weights(layer l) +{ + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ + cuda_pull_array(l.biases_gpu, l.bias_updates, l.n); + cuda_pull_array(l.weights_gpu, l.weight_updates, l.nweights); + if(l.scales) cuda_pull_array(l.scales_gpu, l.scale_updates, l.n); + } else if(l.type == CONNECTED){ + cuda_pull_array(l.biases_gpu, l.bias_updates, l.outputs); + cuda_pull_array(l.weights_gpu, l.weight_updates, l.outputs*l.inputs); + } +} + +void push_weights(layer l) +{ + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ + cuda_push_array(l.biases_gpu, l.biases, l.n); + cuda_push_array(l.weights_gpu, l.weights, l.nweights); + if(l.scales) cuda_push_array(l.scales_gpu, l.scales, l.n); + } else if(l.type == CONNECTED){ + cuda_push_array(l.biases_gpu, l.biases, l.outputs); + cuda_push_array(l.weights_gpu, l.weights, l.outputs*l.inputs); + } +} + +void distribute_weights(layer l, layer base) +{ + if (l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL) { + cuda_push_array(l.biases_gpu, base.biases, l.n); + cuda_push_array(l.weights_gpu, base.weights, l.nweights); + if (base.scales) cuda_push_array(l.scales_gpu, base.scales, l.n); + } else if (l.type == CONNECTED) { + cuda_push_array(l.biases_gpu, base.biases, l.outputs); + cuda_push_array(l.weights_gpu, base.weights, l.outputs*l.inputs); + } +} + + +/* + + void pull_updates(layer l) + { + if(l.type == CONVOLUTIONAL){ + cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.n); + cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.nweights); + if(l.scale_updates) cuda_pull_array(l.scale_updates_gpu, l.scale_updates, l.n); + } else if(l.type == CONNECTED){ + cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.outputs); + cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.outputs*l.inputs); + } + } + + void push_updates(layer l) + { + if(l.type == CONVOLUTIONAL){ + cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.n); + cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.nweights); + if(l.scale_updates) cuda_push_array(l.scale_updates_gpu, l.scale_updates, l.n); + } else if(l.type == CONNECTED){ + cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.outputs); + cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.outputs*l.inputs); + } + } + + void update_layer(layer l, network net) + { + int update_batch = net.batch*net.subdivisions; + float rate = get_current_rate(net); + l.t = get_current_batch(net); + if(l.update_gpu){ + l.update_gpu(l, update_batch, rate*l.learning_rate_scale, net.momentum, net.decay); + } + } + void merge_updates(layer l, layer base) + { + if (l.type == CONVOLUTIONAL) { + axpy_cpu(l.n, 1, l.bias_updates, 1, base.bias_updates, 1); + axpy_cpu(l.nweights, 1, l.weight_updates, 1, base.weight_updates, 1); + if (l.scale_updates) { + axpy_cpu(l.n, 1, l.scale_updates, 1, base.scale_updates, 1); + } + } else if(l.type == CONNECTED) { + axpy_cpu(l.outputs, 1, l.bias_updates, 1, base.bias_updates, 1); + axpy_cpu(l.outputs*l.inputs, 1, l.weight_updates, 1, base.weight_updates, 1); + } + } + + void distribute_updates(layer l, layer base) + { + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ + cuda_push_array(l.bias_updates_gpu, base.bias_updates, l.n); + cuda_push_array(l.weight_updates_gpu, base.weight_updates, l.nweights); + if(base.scale_updates) cuda_push_array(l.scale_updates_gpu, base.scale_updates, l.n); + } else if(l.type == CONNECTED){ + cuda_push_array(l.bias_updates_gpu, base.bias_updates, l.outputs); + cuda_push_array(l.weight_updates_gpu, base.weight_updates, l.outputs*l.inputs); + } + } + */ + +/* + void sync_layer(network *nets, int n, int j) + { + int i; + network net = nets[0]; + layer base = net.layers[j]; + scale_weights(base, 0); + for (i = 0; i < n; ++i) { + cuda_set_device(nets[i].gpu_index); + layer l = nets[i].layers[j]; + pull_weights(l); + merge_weights(l, base); + } + scale_weights(base, 1./n); + for (i = 0; i < n; ++i) { + cuda_set_device(nets[i].gpu_index); + layer l = nets[i].layers[j]; + distribute_weights(l, base); + } + } + */ + +void sync_layer(network **nets, int n, int j) +{ + int i; + network *net = nets[0]; + layer base = net->layers[j]; + scale_weights(base, 0); + for (i = 0; i < n; ++i) { + cuda_set_device(nets[i]->gpu_index); + layer l = nets[i]->layers[j]; + pull_weights(l); + merge_weights(l, base); + } + scale_weights(base, 1./n); + for (i = 0; i < n; ++i) { + cuda_set_device(nets[i]->gpu_index); + layer l = nets[i]->layers[j]; + distribute_weights(l, base); + } +} + +typedef struct{ + network **nets; + int n; + int j; +} sync_args; + +void *sync_layer_thread(void *ptr) +{ + sync_args args = *(sync_args*)ptr; + sync_layer(args.nets, args.n, args.j); + free(ptr); + return 0; +} + +pthread_t sync_layer_in_thread(network **nets, int n, int j) +{ + pthread_t thread; + sync_args *ptr = (sync_args *)calloc(1, sizeof(sync_args)); + ptr->nets = nets; + ptr->n = n; + ptr->j = j; + if(pthread_create(&thread, 0, sync_layer_thread, ptr)) error("Thread creation failed"); + return thread; +} + +void sync_nets(network **nets, int n, int interval) +{ + int j; + int layers = nets[0]->n; + pthread_t *threads = (pthread_t *) calloc(layers, sizeof(pthread_t)); + + *(nets[0]->seen) += interval * (n-1) * nets[0]->batch * nets[0]->subdivisions; + for (j = 0; j < n; ++j){ + *(nets[j]->seen) = *(nets[0]->seen); + } + for (j = 0; j < layers; ++j) { + threads[j] = sync_layer_in_thread(nets, n, j); + } + for (j = 0; j < layers; ++j) { + pthread_join(threads[j], 0); + } + free(threads); +} + +float train_networks(network **nets, int n, data d, int interval) +{ + int i; + int batch = nets[0]->batch; + int subdivisions = nets[0]->subdivisions; + assert(batch * subdivisions * n == d.X.rows); + pthread_t *threads = (pthread_t *) calloc(n, sizeof(pthread_t)); + float *errors = (float *) calloc(n, sizeof(float)); + + float sum = 0; + for(i = 0; i < n; ++i){ + data p = get_data_part(d, i, n); + threads[i] = train_network_in_thread(nets[i], p, errors + i); + } + for(i = 0; i < n; ++i){ + pthread_join(threads[i], 0); + //printf("%f\n", errors[i]); + sum += errors[i]; + } + //cudaDeviceSynchronize(); + if (get_current_batch(nets[0]) % interval == 0) { + printf("Syncing... "); + fflush(stdout); + sync_nets(nets, n, interval); + printf("Done!\n"); + } + //cudaDeviceSynchronize(); + free(threads); + free(errors); + return (float)sum/(n); +} + +void pull_network_output(network *net) +{ + layer l = get_network_output_layer(net); + cuda_pull_array(l.output_gpu, l.output, l.outputs*l.batch); +} + +#endif diff --git a/image.darknet/inst/include/darknet/src/network.h b/image.darknet/inst/include/darknet/src/network.h index e48cbc2..1b0dfd1 100644 --- a/image.darknet/inst/include/darknet/src/network.h +++ b/image.darknet/inst/include/darknet/src/network.h @@ -1,129 +1,29 @@ // Oh boy, why am I about to do this.... #ifndef NETWORK_H #define NETWORK_H +#include "darknet.h" #include "image.h" #include "layer.h" #include "data.h" #include "tree.h" -typedef enum { - CONSTANT, STEP, EXP, POLY, STEPS, SIG, RANDOM -} learning_rate_policy; - -typedef struct network{ - float *workspace; - int n; - int batch; - int *seen; - float epoch; - int subdivisions; - float momentum; - float decay; - layer *layers; - int outputs; - float *output; - learning_rate_policy policy; - - float learning_rate; - float gamma; - float scale; - float power; - int time_steps; - int step; - int max_batches; - float *scales; - int *steps; - int num_steps; - int burn_in; - - int adam; - float B1; - float B2; - float eps; - - int inputs; - int h, w, c; - int max_crop; - int min_crop; - float angle; - float aspect; - float exposure; - float saturation; - float hue; - - int gpu_index; - tree *hierarchy; - - #ifdef GPU - float **input_gpu; - float **truth_gpu; - #endif -} network; - -typedef struct network_state { - float *truth; - float *input; - float *delta; - float *workspace; - int train; - int index; - network net; -} network_state; #ifdef GPU -float train_networks(network *nets, int n, data d, int interval); -void sync_nets(network *nets, int n, int interval); -float train_network_datum_gpu(network net, float *x, float *y); -float *network_predict_gpu(network net, float *input); -float * get_network_output_gpu_layer(network net, int i); -float * get_network_delta_gpu_layer(network net, int i); -float *get_network_output_gpu(network net); -void forward_network_gpu(network net, network_state state); -void backward_network_gpu(network net, network_state state); -void update_network_gpu(network net); +void pull_network_output(network *net); #endif -float get_current_rate(network net); -int get_current_batch(network net); -void free_network(network net); -void compare_networks(network n1, network n2, data d); +void compare_networks(network *n1, network *n2, data d); char *get_layer_string(LAYER_TYPE a); -network make_network(int n); -void forward_network(network net, network_state state); -void backward_network(network net, network_state state); -void update_network(network net); +network *make_network(int n); -float train_network(network net, data d); -float train_network_batch(network net, data d, int n); -float train_network_sgd(network net, data d, int n); -float train_network_datum(network net, float *x, float *y); -matrix network_predict_data(network net, data test); -float *network_predict(network net, float *input); -float network_accuracy(network net, data d); -float *network_accuracies(network net, data d, int n); -float network_accuracy_multi(network net, data d, int n); -void top_predictions(network net, int n, int *index); -float *get_network_output(network net); -float *get_network_output_layer(network net, int i); -float *get_network_delta_layer(network net, int i); -float *get_network_delta(network net); -int get_network_output_size_layer(network net, int i); -int get_network_output_size(network net); -image get_network_image(network net); -image get_network_image_layer(network net, int i); -int get_predicted_class_network(network net); -void print_network(network net); -void visualize_network(network net); +float network_accuracy_multi(network *net, data d, int n); +int get_predicted_class_network(network *net); +void print_network(network *net); int resize_network(network *net, int w, int h); -void set_batch_network(network *net, int b); -int get_network_input_size(network net); -float get_network_cost(network net); - -int get_network_nuisance(network net); -int get_network_background(network net); +void calc_network_cost(network *net); #endif diff --git a/image.darknet/inst/include/darknet/src/network_kernels.cu b/image.darknet/inst/include/darknet/src/network_kernels.cu deleted file mode 100644 index 313cd6d..0000000 --- a/image.darknet/inst/include/darknet/src/network_kernels.cu +++ /dev/null @@ -1,408 +0,0 @@ -#include "cuda_runtime.h" -#include "curand.h" -#include "cublas_v2.h" - -extern "C" { -#include -#include -#include - -#include "network.h" -#include "image.h" -#include "data.h" -#include "utils.h" -#include "parser.h" - -#include "crop_layer.h" -#include "connected_layer.h" -#include "rnn_layer.h" -#include "gru_layer.h" -#include "crnn_layer.h" -#include "detection_layer.h" -#include "region_layer.h" -#include "convolutional_layer.h" -#include "activation_layer.h" -#include "maxpool_layer.h" -#include "reorg_layer.h" -#include "avgpool_layer.h" -#include "normalization_layer.h" -#include "batchnorm_layer.h" -#include "cost_layer.h" -#include "local_layer.h" -#include "softmax_layer.h" -#include "dropout_layer.h" -#include "route_layer.h" -#include "shortcut_layer.h" -#include "blas.h" -} - -float * get_network_output_gpu_layer(network net, int i); -float * get_network_delta_gpu_layer(network net, int i); -float * get_network_output_gpu(network net); - -void forward_network_gpu(network net, network_state state) -{ - state.workspace = net.workspace; - int i; - for(i = 0; i < net.n; ++i){ - state.index = i; - layer l = net.layers[i]; - if(l.delta_gpu){ - fill_ongpu(l.outputs * l.batch, 0, l.delta_gpu, 1); - } - l.forward_gpu(l, state); - state.input = l.output_gpu; - } -} - -void backward_network_gpu(network net, network_state state) -{ - state.workspace = net.workspace; - int i; - float * original_input = state.input; - float * original_delta = state.delta; - for(i = net.n-1; i >= 0; --i){ - state.index = i; - layer l = net.layers[i]; - if(i == 0){ - state.input = original_input; - state.delta = original_delta; - }else{ - layer prev = net.layers[i-1]; - state.input = prev.output_gpu; - state.delta = prev.delta_gpu; - } - l.backward_gpu(l, state); - } -} - -void update_network_gpu(network net) -{ - cuda_set_device(net.gpu_index); - int i; - int update_batch = net.batch*net.subdivisions; - float rate = get_current_rate(net); - for(i = 0; i < net.n; ++i){ - layer l = net.layers[i]; - l.t = get_current_batch(net); - if(l.update_gpu){ - l.update_gpu(l, update_batch, rate, net.momentum, net.decay); - } - } -} - -void forward_backward_network_gpu(network net, float *x, float *y) -{ - network_state state; - state.index = 0; - state.net = net; - int x_size = get_network_input_size(net)*net.batch; - int y_size = get_network_output_size(net)*net.batch; - if(net.layers[net.n-1].truths) y_size = net.layers[net.n-1].truths*net.batch; - if(!*net.input_gpu){ - *net.input_gpu = cuda_make_array(x, x_size); - *net.truth_gpu = cuda_make_array(y, y_size); - }else{ - cuda_push_array(*net.input_gpu, x, x_size); - cuda_push_array(*net.truth_gpu, y, y_size); - } - state.input = *net.input_gpu; - state.delta = 0; - state.truth = *net.truth_gpu; - state.train = 1; - forward_network_gpu(net, state); - backward_network_gpu(net, state); -} - -float train_network_datum_gpu(network net, float *x, float *y) -{ - *net.seen += net.batch; - forward_backward_network_gpu(net, x, y); - float error = get_network_cost(net); - if (((*net.seen) / net.batch) % net.subdivisions == 0) update_network_gpu(net); - - return error; -} - -typedef struct { - network net; - data d; - float *err; -} train_args; - -void *train_thread(void *ptr) -{ - train_args args = *(train_args*)ptr; - free(ptr); - cuda_set_device(args.net.gpu_index); - *args.err = train_network(args.net, args.d); - return 0; -} - -pthread_t train_network_in_thread(network net, data d, float *err) -{ - pthread_t thread; - train_args *ptr = (train_args *)calloc(1, sizeof(train_args)); - ptr->net = net; - ptr->d = d; - ptr->err = err; - if(pthread_create(&thread, 0, train_thread, ptr)) error("Thread creation failed"); - return thread; -} - -void pull_updates(layer l) -{ - if(l.type == CONVOLUTIONAL){ - cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.n); - cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.n*l.size*l.size*l.c); - if(l.scale_updates) cuda_pull_array(l.scale_updates_gpu, l.scale_updates, l.n); - } else if(l.type == CONNECTED){ - cuda_pull_array(l.bias_updates_gpu, l.bias_updates, l.outputs); - cuda_pull_array(l.weight_updates_gpu, l.weight_updates, l.outputs*l.inputs); - } -} - -void push_updates(layer l) -{ - if(l.type == CONVOLUTIONAL){ - cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.n); - cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.n*l.size*l.size*l.c); - if(l.scale_updates) cuda_push_array(l.scale_updates_gpu, l.scale_updates, l.n); - } else if(l.type == CONNECTED){ - cuda_push_array(l.bias_updates_gpu, l.bias_updates, l.outputs); - cuda_push_array(l.weight_updates_gpu, l.weight_updates, l.outputs*l.inputs); - } -} - -void update_layer(layer l, network net) -{ - int update_batch = net.batch*net.subdivisions; - float rate = get_current_rate(net); - l.t = get_current_batch(net); - if(l.update_gpu){ - l.update_gpu(l, update_batch, rate, net.momentum, net.decay); - } -} - -void merge_weights(layer l, layer base) -{ - if (l.type == CONVOLUTIONAL) { - axpy_cpu(l.n, 1, l.biases, 1, base.biases, 1); - axpy_cpu(l.n*l.size*l.size*l.c, 1, l.weights, 1, base.weights, 1); - if (l.scales) { - axpy_cpu(l.n, 1, l.scales, 1, base.scales, 1); - } - } else if(l.type == CONNECTED) { - axpy_cpu(l.outputs, 1, l.biases, 1, base.biases, 1); - axpy_cpu(l.outputs*l.inputs, 1, l.weights, 1, base.weights, 1); - } -} - -void scale_weights(layer l, float s) -{ - if (l.type == CONVOLUTIONAL) { - scal_cpu(l.n, s, l.biases, 1); - scal_cpu(l.n*l.size*l.size*l.c, s, l.weights, 1); - if (l.scales) { - scal_cpu(l.n, s, l.scales, 1); - } - } else if(l.type == CONNECTED) { - scal_cpu(l.outputs, s, l.biases, 1); - scal_cpu(l.outputs*l.inputs, s, l.weights, 1); - } -} - - -void pull_weights(layer l) -{ - if(l.type == CONVOLUTIONAL){ - cuda_pull_array(l.biases_gpu, l.biases, l.n); - cuda_pull_array(l.weights_gpu, l.weights, l.n*l.size*l.size*l.c); - if(l.scales) cuda_pull_array(l.scales_gpu, l.scales, l.n); - } else if(l.type == CONNECTED){ - cuda_pull_array(l.biases_gpu, l.biases, l.outputs); - cuda_pull_array(l.weights_gpu, l.weights, l.outputs*l.inputs); - } -} - -void push_weights(layer l) -{ - if(l.type == CONVOLUTIONAL){ - cuda_push_array(l.biases_gpu, l.biases, l.n); - cuda_push_array(l.weights_gpu, l.weights, l.n*l.size*l.size*l.c); - if(l.scales) cuda_push_array(l.scales_gpu, l.scales, l.n); - } else if(l.type == CONNECTED){ - cuda_push_array(l.biases_gpu, l.biases, l.outputs); - cuda_push_array(l.weights_gpu, l.weights, l.outputs*l.inputs); - } -} - -void distribute_weights(layer l, layer base) -{ - if(l.type == CONVOLUTIONAL){ - cuda_push_array(l.biases_gpu, base.biases, l.n); - cuda_push_array(l.weights_gpu, base.weights, l.n*l.size*l.size*l.c); - if(base.scales) cuda_push_array(l.scales_gpu, base.scales, l.n); - } else if(l.type == CONNECTED){ - cuda_push_array(l.biases_gpu, base.biases, l.outputs); - cuda_push_array(l.weights_gpu, base.weights, l.outputs*l.inputs); - } -} - - -void merge_updates(layer l, layer base) -{ - if (l.type == CONVOLUTIONAL) { - axpy_cpu(l.n, 1, l.bias_updates, 1, base.bias_updates, 1); - axpy_cpu(l.n*l.size*l.size*l.c, 1, l.weight_updates, 1, base.weight_updates, 1); - if (l.scale_updates) { - axpy_cpu(l.n, 1, l.scale_updates, 1, base.scale_updates, 1); - } - } else if(l.type == CONNECTED) { - axpy_cpu(l.outputs, 1, l.bias_updates, 1, base.bias_updates, 1); - axpy_cpu(l.outputs*l.inputs, 1, l.weight_updates, 1, base.weight_updates, 1); - } -} - -void distribute_updates(layer l, layer base) -{ - if(l.type == CONVOLUTIONAL){ - cuda_push_array(l.bias_updates_gpu, base.bias_updates, l.n); - cuda_push_array(l.weight_updates_gpu, base.weight_updates, l.n*l.size*l.size*l.c); - if(base.scale_updates) cuda_push_array(l.scale_updates_gpu, base.scale_updates, l.n); - } else if(l.type == CONNECTED){ - cuda_push_array(l.bias_updates_gpu, base.bias_updates, l.outputs); - cuda_push_array(l.weight_updates_gpu, base.weight_updates, l.outputs*l.inputs); - } -} - -void sync_layer(network *nets, int n, int j) -{ - //printf("Syncing layer %d\n", j); - int i; - network net = nets[0]; - layer base = net.layers[j]; - cuda_set_device(net.gpu_index); - pull_weights(base); - for (i = 1; i < n; ++i) { - cuda_set_device(nets[i].gpu_index); - layer l = nets[i].layers[j]; - pull_weights(l); - merge_weights(l, base); - } - scale_weights(base, 1./n); - for (i = 0; i < n; ++i) { - cuda_set_device(nets[i].gpu_index); - layer l = nets[i].layers[j]; - distribute_weights(l, base); - } - //printf("Done syncing layer %d\n", j); -} - -typedef struct{ - network *nets; - int n; - int j; -} sync_args; - -void *sync_layer_thread(void *ptr) -{ - sync_args args = *(sync_args*)ptr; - sync_layer(args.nets, args.n, args.j); - free(ptr); - return 0; -} - -pthread_t sync_layer_in_thread(network *nets, int n, int j) -{ - pthread_t thread; - sync_args *ptr = (sync_args *)calloc(1, sizeof(sync_args)); - ptr->nets = nets; - ptr->n = n; - ptr->j = j; - if(pthread_create(&thread, 0, sync_layer_thread, ptr)) error("Thread creation failed"); - return thread; -} - -void sync_nets(network *nets, int n, int interval) -{ - int j; - int layers = nets[0].n; - pthread_t *threads = (pthread_t *) calloc(layers, sizeof(pthread_t)); - - *nets[0].seen += interval * (n-1) * nets[0].batch * nets[0].subdivisions; - for (j = 0; j < n; ++j){ - *nets[j].seen = *nets[0].seen; - } - for (j = 0; j < layers; ++j) { - threads[j] = sync_layer_in_thread(nets, n, j); - } - for (j = 0; j < layers; ++j) { - pthread_join(threads[j], 0); - } - free(threads); -} - -float train_networks(network *nets, int n, data d, int interval) -{ - int i; - int batch = nets[0].batch; - int subdivisions = nets[0].subdivisions; - assert(batch * subdivisions * n == d.X.rows); - pthread_t *threads = (pthread_t *) calloc(n, sizeof(pthread_t)); - float *errors = (float *) calloc(n, sizeof(float)); - - float sum = 0; - for(i = 0; i < n; ++i){ - data p = get_data_part(d, i, n); - threads[i] = train_network_in_thread(nets[i], p, errors + i); - } - for(i = 0; i < n; ++i){ - pthread_join(threads[i], 0); - //printf("%f\n", errors[i]); - sum += errors[i]; - } - //cudaDeviceSynchronize(); - if (get_current_batch(nets[0]) % interval == 0) { - printf("Syncing... "); - fflush(stdout); - sync_nets(nets, n, interval); - printf("Done!\n"); - } - //cudaDeviceSynchronize(); - free(threads); - free(errors); - return (float)sum/(n); -} - -float *get_network_output_layer_gpu(network net, int i) -{ - layer l = net.layers[i]; - if(l.type != REGION) cuda_pull_array(l.output_gpu, l.output, l.outputs*l.batch); - return l.output; -} - -float *get_network_output_gpu(network net) -{ - int i; - for(i = net.n-1; i > 0; --i) if(net.layers[i].type != COST) break; - return get_network_output_layer_gpu(net, i); -} - -float *network_predict_gpu(network net, float *input) -{ - cuda_set_device(net.gpu_index); - int size = get_network_input_size(net) * net.batch; - network_state state; - state.index = 0; - state.net = net; - state.input = cuda_make_array(input, size); - state.truth = 0; - state.train = 0; - state.delta = 0; - forward_network_gpu(net, state); - float *out = get_network_output_gpu(net); - cuda_free(state.input); - return out; -} - diff --git a/image.darknet/inst/include/darknet/src/normalization_layer.c b/image.darknet/inst/include/darknet/src/normalization_layer.c index 069a079..424714f 100644 --- a/image.darknet/inst/include/darknet/src/normalization_layer.c +++ b/image.darknet/inst/include/darknet/src/normalization_layer.c @@ -1,5 +1,6 @@ #include "normalization_layer.h" #include "blas.h" + #include layer make_normalization_layer(int batch, int w, int h, int c, int size, float alpha, float beta, float kappa) @@ -62,7 +63,7 @@ void resize_normalization_layer(layer *layer, int w, int h) #endif } -void forward_normalization_layer(const layer layer, network_state state) +void forward_normalization_layer(const layer layer, network net) { int k,b; int w = layer.w; @@ -73,7 +74,7 @@ void forward_normalization_layer(const layer layer, network_state state) for(b = 0; b < layer.batch; ++b){ float *squared = layer.squared + w*h*c*b; float *norms = layer.norms + w*h*c*b; - float *input = state.input + w*h*c*b; + float *input = net.input + w*h*c*b; pow_cpu(w*h*c, 2, input, 1, squared, 1); const_cpu(w*h, layer.kappa, norms, 1); @@ -90,10 +91,10 @@ void forward_normalization_layer(const layer layer, network_state state) } } pow_cpu(w*h*c*layer.batch, -layer.beta, layer.norms, 1, layer.output, 1); - mul_cpu(w*h*c*layer.batch, state.input, 1, layer.output, 1); + mul_cpu(w*h*c*layer.batch, net.input, 1, layer.output, 1); } -void backward_normalization_layer(const layer layer, network_state state) +void backward_normalization_layer(const layer layer, network net) { // TODO This is approximate ;-) // Also this should add in to delta instead of overwritting. @@ -101,50 +102,50 @@ void backward_normalization_layer(const layer layer, network_state state) int w = layer.w; int h = layer.h; int c = layer.c; - pow_cpu(w*h*c*layer.batch, -layer.beta, layer.norms, 1, state.delta, 1); - mul_cpu(w*h*c*layer.batch, layer.delta, 1, state.delta, 1); + pow_cpu(w*h*c*layer.batch, -layer.beta, layer.norms, 1, net.delta, 1); + mul_cpu(w*h*c*layer.batch, layer.delta, 1, net.delta, 1); } #ifdef GPU -void forward_normalization_layer_gpu(const layer layer, network_state state) +void forward_normalization_layer_gpu(const layer layer, network net) { int k,b; int w = layer.w; int h = layer.h; int c = layer.c; - scal_ongpu(w*h*c*layer.batch, 0, layer.squared_gpu, 1); + scal_gpu(w*h*c*layer.batch, 0, layer.squared_gpu, 1); for(b = 0; b < layer.batch; ++b){ float *squared = layer.squared_gpu + w*h*c*b; float *norms = layer.norms_gpu + w*h*c*b; - float *input = state.input + w*h*c*b; - pow_ongpu(w*h*c, 2, input, 1, squared, 1); + float *input = net.input_gpu + w*h*c*b; + pow_gpu(w*h*c, 2, input, 1, squared, 1); - const_ongpu(w*h, layer.kappa, norms, 1); + const_gpu(w*h, layer.kappa, norms, 1); for(k = 0; k < layer.size/2; ++k){ - axpy_ongpu(w*h, layer.alpha, squared + w*h*k, 1, norms, 1); + axpy_gpu(w*h, layer.alpha, squared + w*h*k, 1, norms, 1); } for(k = 1; k < layer.c; ++k){ - copy_ongpu(w*h, norms + w*h*(k-1), 1, norms + w*h*k, 1); + copy_gpu(w*h, norms + w*h*(k-1), 1, norms + w*h*k, 1); int prev = k - ((layer.size-1)/2) - 1; int next = k + (layer.size/2); - if(prev >= 0) axpy_ongpu(w*h, -layer.alpha, squared + w*h*prev, 1, norms + w*h*k, 1); - if(next < layer.c) axpy_ongpu(w*h, layer.alpha, squared + w*h*next, 1, norms + w*h*k, 1); + if(prev >= 0) axpy_gpu(w*h, -layer.alpha, squared + w*h*prev, 1, norms + w*h*k, 1); + if(next < layer.c) axpy_gpu(w*h, layer.alpha, squared + w*h*next, 1, norms + w*h*k, 1); } } - pow_ongpu(w*h*c*layer.batch, -layer.beta, layer.norms_gpu, 1, layer.output_gpu, 1); - mul_ongpu(w*h*c*layer.batch, state.input, 1, layer.output_gpu, 1); + pow_gpu(w*h*c*layer.batch, -layer.beta, layer.norms_gpu, 1, layer.output_gpu, 1); + mul_gpu(w*h*c*layer.batch, net.input_gpu, 1, layer.output_gpu, 1); } -void backward_normalization_layer_gpu(const layer layer, network_state state) +void backward_normalization_layer_gpu(const layer layer, network net) { // TODO This is approximate ;-) int w = layer.w; int h = layer.h; int c = layer.c; - pow_ongpu(w*h*c*layer.batch, -layer.beta, layer.norms_gpu, 1, state.delta, 1); - mul_ongpu(w*h*c*layer.batch, layer.delta_gpu, 1, state.delta, 1); + pow_gpu(w*h*c*layer.batch, -layer.beta, layer.norms_gpu, 1, net.delta_gpu, 1); + mul_gpu(w*h*c*layer.batch, layer.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/inst/include/darknet/src/normalization_layer.h b/image.darknet/inst/include/darknet/src/normalization_layer.h index ab32776..665baa5 100644 --- a/image.darknet/inst/include/darknet/src/normalization_layer.h +++ b/image.darknet/inst/include/darknet/src/normalization_layer.h @@ -7,13 +7,13 @@ layer make_normalization_layer(int batch, int w, int h, int c, int size, float alpha, float beta, float kappa); void resize_normalization_layer(layer *layer, int h, int w); -void forward_normalization_layer(const layer layer, network_state state); -void backward_normalization_layer(const layer layer, network_state state); +void forward_normalization_layer(const layer layer, network net); +void backward_normalization_layer(const layer layer, network net); void visualize_normalization_layer(layer layer, char *window); #ifdef GPU -void forward_normalization_layer_gpu(const layer layer, network_state state); -void backward_normalization_layer_gpu(const layer layer, network_state state); +void forward_normalization_layer_gpu(const layer layer, network net); +void backward_normalization_layer_gpu(const layer layer, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/option_list.c b/image.darknet/inst/include/darknet/src/option_list.c index f935af3..2f52781 100644 --- a/image.darknet/inst/include/darknet/src/option_list.c +++ b/image.darknet/inst/include/darknet/src/option_list.c @@ -32,6 +32,23 @@ list *read_data_cfg(char *filename) return options; } +metadata get_metadata(char *file) +{ + metadata m = {0}; + list *options = read_data_cfg(file); + + char *name_list = option_find_str(options, "names", 0); + if(!name_list) name_list = option_find_str(options, "labels", 0); + if(!name_list) { + fprintf(stderr, "No names or labels found\n"); + } else { + m.names = get_labels(name_list); + } + m.classes = option_find_int(options, "classes", 2); + free_list(options); + return m; +} + int read_option(char *s, list *options) { size_t i; diff --git a/image.darknet/inst/include/darknet/src/option_list.h b/image.darknet/inst/include/darknet/src/option_list.h index 054b3fd..844bd87 100644 --- a/image.darknet/inst/include/darknet/src/option_list.h +++ b/image.darknet/inst/include/darknet/src/option_list.h @@ -9,13 +9,9 @@ typedef struct{ } kvp; -list *read_data_cfg(char *filename); int read_option(char *s, list *options); void option_insert(list *l, char *key, char *val); char *option_find(list *l, char *key); -char *option_find_str(list *l, char *key, char *def); -int option_find_int(list *l, char *key, int def); -int option_find_int_quiet(list *l, char *key, int def); float option_find_float(list *l, char *key, float def); float option_find_float_quiet(list *l, char *key, float def); void option_unused(list *l); diff --git a/image.darknet/inst/include/darknet/src/parser.c b/image.darknet/inst/include/darknet/src/parser.c index 3f39a13..c8141c9 100644 --- a/image.darknet/inst/include/darknet/src/parser.c +++ b/image.darknet/inst/include/darknet/src/parser.c @@ -1,14 +1,17 @@ #include #include #include +#include #include "activation_layer.h" +#include "logistic_layer.h" +#include "l2norm_layer.h" #include "activations.h" -#include "assert.h" #include "avgpool_layer.h" #include "batchnorm_layer.h" #include "blas.h" #include "connected_layer.h" +#include "deconvolutional_layer.h" #include "convolutional_layer.h" #include "cost_layer.h" #include "crnn_layer.h" @@ -23,11 +26,15 @@ #include "option_list.h" #include "parser.h" #include "region_layer.h" +#include "yolo_layer.h" +#include "iseg_layer.h" #include "reorg_layer.h" #include "rnn_layer.h" #include "route_layer.h" +#include "upsample_layer.h" #include "shortcut_layer.h" #include "softmax_layer.h" +#include "lstm_layer.h" #include "utils.h" typedef struct{ @@ -45,14 +52,21 @@ LAYER_TYPE string_to_layer_type(char * type) if (strcmp(type, "[cost]")==0) return COST; if (strcmp(type, "[detection]")==0) return DETECTION; if (strcmp(type, "[region]")==0) return REGION; + if (strcmp(type, "[yolo]")==0) return YOLO; + if (strcmp(type, "[iseg]")==0) return ISEG; if (strcmp(type, "[local]")==0) return LOCAL; if (strcmp(type, "[conv]")==0 || strcmp(type, "[convolutional]")==0) return CONVOLUTIONAL; + if (strcmp(type, "[deconv]")==0 + || strcmp(type, "[deconvolutional]")==0) return DECONVOLUTIONAL; if (strcmp(type, "[activation]")==0) return ACTIVE; + if (strcmp(type, "[logistic]")==0) return LOGXENT; + if (strcmp(type, "[l2norm]")==0) return L2NORM; if (strcmp(type, "[net]")==0 || strcmp(type, "[network]")==0) return NETWORK; if (strcmp(type, "[crnn]")==0) return CRNN; if (strcmp(type, "[gru]")==0) return GRU; + if (strcmp(type, "[lstm]") == 0) return LSTM; if (strcmp(type, "[rnn]")==0) return RNN; if (strcmp(type, "[conn]")==0 || strcmp(type, "[connected]")==0) return CONNECTED; @@ -68,6 +82,7 @@ LAYER_TYPE string_to_layer_type(char * type) if (strcmp(type, "[soft]")==0 || strcmp(type, "[softmax]")==0) return SOFTMAX; if (strcmp(type, "[route]")==0) return ROUTE; + if (strcmp(type, "[upsample]")==0) return UPSAMPLE; return BLANK; } @@ -111,7 +126,7 @@ typedef struct size_params{ int c; int index; int time_steps; - network net; + network *net; } size_params; local_layer parse_local(list *options, size_params params) @@ -135,6 +150,32 @@ local_layer parse_local(list *options, size_params params) return layer; } +layer parse_deconvolutional(list *options, size_params params) +{ + int n = option_find_int(options, "filters",1); + int size = option_find_int(options, "size",1); + int stride = option_find_int(options, "stride",1); + + char *activation_s = option_find_str(options, "activation", "logistic"); + ACTIVATION activation = get_activation(activation_s); + + int batch,h,w,c; + h = params.h; + w = params.w; + c = params.c; + batch=params.batch; + if(!(h && w && c)) error("Layer before deconvolutional layer must output image."); + int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); + int pad = option_find_int_quiet(options, "pad",0); + int padding = option_find_int_quiet(options, "padding",0); + if(pad) padding = size/2; + + layer l = make_deconvolutional_layer(batch,h,w,c,n,size,stride,padding, activation, batch_normalize, params.net->adam); + + return l; +} + + convolutional_layer parse_convolutional(list *options, size_params params) { int n = option_find_int(options, "filters",1); @@ -142,6 +183,7 @@ convolutional_layer parse_convolutional(list *options, size_params params) int stride = option_find_int(options, "stride",1); int pad = option_find_int_quiet(options, "pad",0); int padding = option_find_int_quiet(options, "padding",0); + int groups = option_find_int_quiet(options, "groups", 1); if(pad) padding = size/2; char *activation_s = option_find_str(options, "activation", "logistic"); @@ -157,14 +199,9 @@ convolutional_layer parse_convolutional(list *options, size_params params) int binary = option_find_int_quiet(options, "binary", 0); int xnor = option_find_int_quiet(options, "xnor", 0); - convolutional_layer layer = make_convolutional_layer(batch,h,w,c,n,size,stride,padding,activation, batch_normalize, binary, xnor, params.net.adam); + convolutional_layer layer = make_convolutional_layer(batch,h,w,c,n,groups,size,stride,padding,activation, batch_normalize, binary, xnor, params.net->adam); layer.flipped = option_find_int_quiet(options, "flipped", 0); layer.dot = option_find_float_quiet(options, "dot", 0); - if(params.net.adam){ - layer.B1 = params.net.B1; - layer.B2 = params.net.B2; - layer.eps = params.net.eps; - } return layer; } @@ -187,13 +224,11 @@ layer parse_crnn(list *options, size_params params) layer parse_rnn(list *options, size_params params) { int output = option_find_int(options, "output",1); - int hidden = option_find_int(options, "hidden",1); char *activation_s = option_find_str(options, "activation", "logistic"); ACTIVATION activation = get_activation(activation_s); int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); - int logistic = option_find_int_quiet(options, "logistic", 0); - layer l = make_rnn_layer(params.batch, params.inputs, hidden, output, params.time_steps, activation, batch_normalize, logistic); + layer l = make_rnn_layer(params.batch, params.inputs, output, params.time_steps, activation, batch_normalize, params.net->adam); l.shortcut = option_find_int_quiet(options, "shortcut", 0); @@ -205,31 +240,114 @@ layer parse_gru(list *options, size_params params) int output = option_find_int(options, "output",1); int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); - layer l = make_gru_layer(params.batch, params.inputs, output, params.time_steps, batch_normalize); + layer l = make_gru_layer(params.batch, params.inputs, output, params.time_steps, batch_normalize, params.net->adam); + l.tanh = option_find_int_quiet(options, "tanh", 0); + + return l; +} + +layer parse_lstm(list *options, size_params params) +{ + int output = option_find_int(options, "output", 1); + int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); + + layer l = make_lstm_layer(params.batch, params.inputs, output, params.time_steps, batch_normalize, params.net->adam); return l; } -connected_layer parse_connected(list *options, size_params params) +layer parse_connected(list *options, size_params params) { int output = option_find_int(options, "output",1); char *activation_s = option_find_str(options, "activation", "logistic"); ACTIVATION activation = get_activation(activation_s); int batch_normalize = option_find_int_quiet(options, "batch_normalize", 0); - connected_layer layer = make_connected_layer(params.batch, params.inputs, output, activation, batch_normalize); - - return layer; + layer l = make_connected_layer(params.batch, params.inputs, output, activation, batch_normalize, params.net->adam); + return l; } -softmax_layer parse_softmax(list *options, size_params params) +layer parse_softmax(list *options, size_params params) { int groups = option_find_int_quiet(options, "groups",1); - softmax_layer layer = make_softmax_layer(params.batch, params.inputs, groups); - layer.temperature = option_find_float_quiet(options, "temperature", 1); + layer l = make_softmax_layer(params.batch, params.inputs, groups); + l.temperature = option_find_float_quiet(options, "temperature", 1); char *tree_file = option_find_str(options, "tree", 0); - if (tree_file) layer.softmax_tree = read_tree(tree_file); - return layer; + if (tree_file) l.softmax_tree = read_tree(tree_file); + l.w = params.w; + l.h = params.h; + l.c = params.c; + l.spatial = option_find_float_quiet(options, "spatial", 0); + l.noloss = option_find_int_quiet(options, "noloss", 0); + return l; +} + +int *parse_yolo_mask(char *a, int *num) +{ + int *mask = 0; + if(a){ + int len = strlen(a); + int n = 1; + int i; + for(i = 0; i < len; ++i){ + if (a[i] == ',') ++n; + } + mask = calloc(n, sizeof(int)); + for(i = 0; i < n; ++i){ + int val = atoi(a); + mask[i] = val; + a = strchr(a, ',')+1; + } + *num = n; + } + return mask; +} + +layer parse_yolo(list *options, size_params params) +{ + int classes = option_find_int(options, "classes", 20); + int total = option_find_int(options, "num", 1); + int num = total; + + char *a = option_find_str(options, "mask", 0); + int *mask = parse_yolo_mask(a, &num); + layer l = make_yolo_layer(params.batch, params.w, params.h, num, total, mask, classes); + assert(l.outputs == params.inputs); + + l.max_boxes = option_find_int_quiet(options, "max",90); + l.jitter = option_find_float(options, "jitter", .2); + + l.ignore_thresh = option_find_float(options, "ignore_thresh", .5); + l.truth_thresh = option_find_float(options, "truth_thresh", 1); + l.random = option_find_int_quiet(options, "random", 0); + + char *map_file = option_find_str(options, "map", 0); + if (map_file) l.map = read_map(map_file); + + a = option_find_str(options, "anchors", 0); + if(a){ + int len = strlen(a); + int n = 1; + int i; + for(i = 0; i < len; ++i){ + if (a[i] == ',') ++n; + } + for(i = 0; i < n; ++i){ + float bias = atof(a); + l.biases[i] = bias; + a = strchr(a, ',')+1; + } + } + return l; +} + +layer parse_iseg(list *options, size_params params) +{ + int classes = option_find_int(options, "classes", 20); + int ids = option_find_int(options, "ids", 32); + layer l = make_iseg_layer(params.batch, params.w, params.h, classes, ids); + assert(l.outputs == params.inputs); + return l; } layer parse_region(list *options, size_params params) @@ -245,6 +363,7 @@ layer parse_region(list *options, size_params params) l.sqrt = option_find_int_quiet(options, "sqrt", 0); l.softmax = option_find_int(options, "softmax", 0); + l.background = option_find_int_quiet(options, "background", 0); l.max_boxes = option_find_int_quiet(options, "max",30); l.jitter = option_find_float(options, "jitter", .2); l.rescore = option_find_int_quiet(options, "rescore",0); @@ -257,6 +376,7 @@ layer parse_region(list *options, size_params params) l.coord_scale = option_find_float(options, "coord_scale", 1); l.object_scale = option_find_float(options, "object_scale", 1); l.noobject_scale = option_find_float(options, "noobject_scale", 1); + l.mask_scale = option_find_float(options, "mask_scale", 1); l.class_scale = option_find_float(options, "class_scale", 1); l.bias_match = option_find_int_quiet(options, "bias_match",0); @@ -281,6 +401,7 @@ layer parse_region(list *options, size_params params) } return l; } + detection_layer parse_detection(list *options, size_params params) { int coords = option_find_int(options, "coords", 1); @@ -293,7 +414,7 @@ detection_layer parse_detection(list *options, size_params params) layer.softmax = option_find_int(options, "softmax", 0); layer.sqrt = option_find_int(options, "sqrt", 0); - layer.max_boxes = option_find_int_quiet(options, "max",30); + layer.max_boxes = option_find_int_quiet(options, "max",90); layer.coord_scale = option_find_float(options, "coord_scale", 1); layer.forced = option_find_int(options, "forced", 0); layer.object_scale = option_find_float(options, "object_scale", 1); @@ -312,6 +433,8 @@ cost_layer parse_cost(list *options, size_params params) float scale = option_find_float_quiet(options, "scale",1); cost_layer layer = make_cost_layer(params.batch, params.inputs, type, scale); layer.ratio = option_find_float_quiet(options, "ratio",0); + layer.noobject_scale = option_find_float_quiet(options, "noobj", 1); + layer.thresh = option_find_float_quiet(options, "thresh",0); return layer; } @@ -343,6 +466,8 @@ layer parse_reorg(list *options, size_params params) { int stride = option_find_int(options, "stride",1); int reverse = option_find_int_quiet(options, "reverse",0); + int flatten = option_find_int_quiet(options, "flatten",0); + int extra = option_find_int_quiet(options, "extra",0); int batch,h,w,c; h = params.h; @@ -351,7 +476,7 @@ layer parse_reorg(list *options, size_params params) batch=params.batch; if(!(h && w && c)) error("Layer before reorg layer must output image."); - layer layer = make_reorg_layer(batch,w,h,c,stride,reverse); + layer layer = make_reorg_layer(batch,w,h,c,stride,reverse, flatten, extra); return layer; } @@ -359,7 +484,7 @@ maxpool_layer parse_maxpool(list *options, size_params params) { int stride = option_find_int(options, "stride",1); int size = option_find_int(options, "size",stride); - int padding = option_find_int_quiet(options, "padding", (size-1)/2); + int padding = option_find_int_quiet(options, "padding", size-1); int batch,h,w,c; h = params.h; @@ -411,24 +536,45 @@ layer parse_batchnorm(list *options, size_params params) return l; } -layer parse_shortcut(list *options, size_params params, network net) +layer parse_shortcut(list *options, size_params params, network *net) { - char *l = option_find(options, "from"); + char *l = option_find(options, "from"); int index = atoi(l); if(index < 0) index = params.index + index; int batch = params.batch; - layer from = net.layers[index]; + layer from = net->layers[index]; layer s = make_shortcut_layer(batch, index, params.w, params.h, params.c, from.out_w, from.out_h, from.out_c); char *activation_s = option_find_str(options, "activation", "linear"); ACTIVATION activation = get_activation(activation_s); s.activation = activation; + s.alpha = option_find_float_quiet(options, "alpha", 1); + s.beta = option_find_float_quiet(options, "beta", 1); return s; } +layer parse_l2norm(list *options, size_params params) +{ + layer l = make_l2norm_layer(params.batch, params.inputs); + l.h = l.out_h = params.h; + l.w = l.out_w = params.w; + l.c = l.out_c = params.c; + return l; +} + + +layer parse_logistic(list *options, size_params params) +{ + layer l = make_logistic_layer(params.batch, params.inputs); + l.h = l.out_h = params.h; + l.w = l.out_w = params.w; + l.c = l.out_c = params.c; + return l; +} + layer parse_activation(list *options, size_params params) { char *activation_s = option_find_str(options, "activation", "linear"); @@ -436,19 +582,25 @@ layer parse_activation(list *options, size_params params) layer l = make_activation_layer(params.batch, params.inputs, activation); - l.out_h = params.h; - l.out_w = params.w; - l.out_c = params.c; - l.h = params.h; - l.w = params.w; - l.c = params.c; + l.h = l.out_h = params.h; + l.w = l.out_w = params.w; + l.c = l.out_c = params.c; + + return l; +} + +layer parse_upsample(list *options, size_params params, network *net) +{ + int stride = option_find_int(options, "stride",2); + layer l = make_upsample_layer(params.batch, params.w, params.h, params.c, stride); + l.scale = option_find_float_quiet(options, "scale", 1); return l; } -route_layer parse_route(list *options, size_params params, network net) +route_layer parse_route(list *options, size_params params, network *net) { - char *l = option_find(options, "layers"); + char *l = option_find(options, "layers"); int len = strlen(l); if(!l) error("Route Layer must specify input layers"); int n = 1; @@ -464,19 +616,19 @@ route_layer parse_route(list *options, size_params params, network net) l = strchr(l, ',')+1; if(index < 0) index = params.index + index; layers[i] = index; - sizes[i] = net.layers[index].outputs; + sizes[i] = net->layers[index].outputs; } int batch = params.batch; route_layer layer = make_route_layer(batch, n, layers, sizes); - convolutional_layer first = net.layers[layers[0]]; + convolutional_layer first = net->layers[layers[0]]; layer.out_w = first.out_w; layer.out_h = first.out_h; layer.out_c = first.out_c; for(i = 1; i < n; ++i){ int index = layers[i]; - convolutional_layer next = net.layers[index]; + convolutional_layer next = net->layers[index]; if(next.out_w == first.out_w && next.out_h == first.out_h){ layer.out_c += next.out_c; }else{ @@ -508,15 +660,17 @@ void parse_net_options(list *options, network *net) net->decay = option_find_float(options, "decay", .0001); int subdivs = option_find_int(options, "subdivisions",1); net->time_steps = option_find_int_quiet(options, "time_steps",1); + net->notruth = option_find_int_quiet(options, "notruth",0); net->batch /= subdivs; net->batch *= net->time_steps; net->subdivisions = subdivs; + net->random = option_find_int_quiet(options, "random", 0); net->adam = option_find_int_quiet(options, "adam", 0); if(net->adam){ net->B1 = option_find_float(options, "B1", .9); net->B2 = option_find_float(options, "B2", .999); - net->eps = option_find_float(options, "eps", .000001); + net->eps = option_find_float(options, "eps", .0000001); } net->h = option_find_int_quiet(options, "height",0); @@ -525,6 +679,10 @@ void parse_net_options(list *options, network *net) net->inputs = option_find_int_quiet(options, "inputs", net->h * net->w * net->c); net->max_crop = option_find_int_quiet(options, "max_crop",net->w*2); net->min_crop = option_find_int_quiet(options, "min_crop",net->w); + net->max_ratio = option_find_float_quiet(options, "max_ratio", (float) net->max_crop / net->w); + net->min_ratio = option_find_float_quiet(options, "min_ratio", (float) net->min_crop / net->w); + net->center = option_find_int_quiet(options, "center",0); + net->clip = option_find_float_quiet(options, "clip", 0); net->angle = option_find_float_quiet(options, "angle", 0); net->aspect = option_find_float_quiet(options, "aspect", 1); @@ -537,12 +695,13 @@ void parse_net_options(list *options, network *net) char *policy_s = option_find_str(options, "policy", "constant"); net->policy = get_policy(policy_s); net->burn_in = option_find_int_quiet(options, "burn_in", 0); + net->power = option_find_float_quiet(options, "power", 4); if(net->policy == STEP){ net->step = option_find_int(options, "step", 1); net->scale = option_find_float(options, "scale", 1); } else if (net->policy == STEPS){ - char *l = option_find(options, "steps"); - char *p = option_find(options, "scales"); + char *l = option_find(options, "steps"); + char *p = option_find(options, "scales"); if(!l || !p) error("STEPS policy must have steps and scales in cfg file"); int len = strlen(l); @@ -570,7 +729,6 @@ void parse_net_options(list *options, network *net) net->gamma = option_find_float(options, "gamma", 1); net->step = option_find_int(options, "step", 1); } else if (net->policy == POLY || net->policy == RANDOM){ - net->power = option_find_float(options, "power", 1); } net->max_batches = option_find_int(options, "max_batches", 0); } @@ -581,26 +739,26 @@ int is_network(section *s) || strcmp(s->type, "[network]")==0); } -network parse_network_cfg(char *filename) +network *parse_network_cfg(char *filename) { list *sections = read_cfg(filename); node *n = sections->front; if(!n) error("Config file has no sections"); - network net = make_network(sections->size - 1); - net.gpu_index = gpu_index; + network *net = make_network(sections->size - 1); + net->gpu_index = gpu_index; size_params params; section *s = (section *)n->val; list *options = s->options; if(!is_network(s)) error("First section must be [net] or [network]"); - parse_net_options(options, &net); - - params.h = net.h; - params.w = net.w; - params.c = net.c; - params.inputs = net.inputs; - params.batch = net.batch; - params.time_steps = net.time_steps; + parse_net_options(options, net); + + params.h = net->h; + params.w = net->w; + params.c = net->c; + params.inputs = net->inputs; + params.batch = net->batch; + params.time_steps = net->time_steps; params.net = net; size_t workspace_size = 0; @@ -617,14 +775,22 @@ network parse_network_cfg(char *filename) LAYER_TYPE lt = string_to_layer_type(s->type); if(lt == CONVOLUTIONAL){ l = parse_convolutional(options, params); + }else if(lt == DECONVOLUTIONAL){ + l = parse_deconvolutional(options, params); }else if(lt == LOCAL){ l = parse_local(options, params); }else if(lt == ACTIVE){ l = parse_activation(options, params); + }else if(lt == LOGXENT){ + l = parse_logistic(options, params); + }else if(lt == L2NORM){ + l = parse_l2norm(options, params); }else if(lt == RNN){ l = parse_rnn(options, params); }else if(lt == GRU){ l = parse_gru(options, params); + }else if (lt == LSTM) { + l = parse_lstm(options, params); }else if(lt == CRNN){ l = parse_crnn(options, params); }else if(lt == CONNECTED){ @@ -635,11 +801,15 @@ network parse_network_cfg(char *filename) l = parse_cost(options, params); }else if(lt == REGION){ l = parse_region(options, params); + }else if(lt == YOLO){ + l = parse_yolo(options, params); + }else if(lt == ISEG){ + l = parse_iseg(options, params); }else if(lt == DETECTION){ l = parse_detection(options, params); }else if(lt == SOFTMAX){ l = parse_softmax(options, params); - net.hierarchy = l.softmax_tree; + net->hierarchy = l.softmax_tree; }else if(lt == NORMALIZATION){ l = parse_normalization(options, params); }else if(lt == BATCHNORM){ @@ -652,23 +822,33 @@ network parse_network_cfg(char *filename) l = parse_avgpool(options, params); }else if(lt == ROUTE){ l = parse_route(options, params, net); + }else if(lt == UPSAMPLE){ + l = parse_upsample(options, params, net); }else if(lt == SHORTCUT){ l = parse_shortcut(options, params, net); }else if(lt == DROPOUT){ l = parse_dropout(options, params); - l.output = net.layers[count-1].output; - l.delta = net.layers[count-1].delta; + l.output = net->layers[count-1].output; + l.delta = net->layers[count-1].delta; #ifdef GPU - l.output_gpu = net.layers[count-1].output_gpu; - l.delta_gpu = net.layers[count-1].delta_gpu; + l.output_gpu = net->layers[count-1].output_gpu; + l.delta_gpu = net->layers[count-1].delta_gpu; #endif }else{ fprintf(stderr, "Type not recognized: %s\n", s->type); } + l.clip = net->clip; + l.truth = option_find_int_quiet(options, "truth", 0); + l.onlyforward = option_find_int_quiet(options, "onlyforward", 0); + l.stopbackward = option_find_int_quiet(options, "stopbackward", 0); + l.dontsave = option_find_int_quiet(options, "dontsave", 0); l.dontload = option_find_int_quiet(options, "dontload", 0); + l.numload = option_find_int_quiet(options, "numload", 0); l.dontloadscales = option_find_int_quiet(options, "dontloadscales", 0); + l.learning_rate_scale = option_find_float_quiet(options, "learning_rate", 1); + l.smooth = option_find_float_quiet(options, "smooth", 0); option_unused(options); - net.layers[count] = l; + net->layers[count] = l; if (l.workspace_size > workspace_size) workspace_size = l.workspace_size; free_section(s); n = n->next; @@ -679,20 +859,30 @@ network parse_network_cfg(char *filename) params.c = l.out_c; params.inputs = l.outputs; } - } + } free_list(sections); - net.outputs = get_network_output_size(net); - net.output = get_network_output(net); + layer out = get_network_output_layer(net); + net->outputs = out.outputs; + net->truths = out.outputs; + if(net->layers[net->n-1].truths) net->truths = net->layers[net->n-1].truths; + net->output = out.output; + net->input = calloc(net->inputs*net->batch, sizeof(float)); + net->truth = calloc(net->truths*net->batch, sizeof(float)); +#ifdef GPU + net->output_gpu = out.output_gpu; + net->input_gpu = cuda_make_array(net->input, net->inputs*net->batch); + net->truth_gpu = cuda_make_array(net->truth, net->truths*net->batch); +#endif if(workspace_size){ //printf("%ld\n", workspace_size); #ifdef GPU if(gpu_index >= 0){ - net.workspace = cuda_make_array(0, (workspace_size-1)/sizeof(float)+1); + net->workspace = cuda_make_array(0, (workspace_size-1)/sizeof(float)+1); }else { - net.workspace = calloc(1, workspace_size); + net->workspace = calloc(1, workspace_size); } #else - net.workspace = calloc(1, workspace_size); + net->workspace = calloc(1, workspace_size); #endif } return net; @@ -704,7 +894,7 @@ list *read_cfg(char *filename) if(file == 0) file_error(filename); char *line; int nu = 0; - list *sections = make_list(); + list *options = make_list(); section *current = 0; while((line=fgetl(file)) != 0){ ++ nu; @@ -712,7 +902,7 @@ list *read_cfg(char *filename) switch(line[0]){ case '[': current = malloc(sizeof(section)); - list_insert(sections, current); + list_insert(options, current); current->options = make_list(); current->type = line; break; @@ -730,7 +920,7 @@ list *read_cfg(char *filename) } } fclose(file); - return sections; + return options; } void save_convolutional_weights_binary(layer l, FILE *fp) @@ -776,7 +966,7 @@ void save_convolutional_weights(layer l, FILE *fp) pull_convolutional_layer(l); } #endif - int num = l.n*l.c*l.size*l.size; + int num = l.nweights; fwrite(l.biases, sizeof(float), l.n, fp); if (l.batch_normalize){ fwrite(l.scales, sizeof(float), l.n, fp); @@ -784,10 +974,6 @@ void save_convolutional_weights(layer l, FILE *fp) fwrite(l.rolling_variance, sizeof(float), l.n, fp); } fwrite(l.weights, sizeof(float), num, fp); - if(l.adam){ - fwrite(l.m, sizeof(float), num, fp); - fwrite(l.v, sizeof(float), num, fp); - } } void save_batchnorm_weights(layer l, FILE *fp) @@ -818,11 +1004,11 @@ void save_connected_weights(layer l, FILE *fp) } } -void save_weights_upto(network net, char *filename, int cutoff) +void save_weights_upto(network *net, char *filename, int cutoff) { #ifdef GPU - if(net.gpu_index >= 0){ - cuda_set_device(net.gpu_index); + if(net->gpu_index >= 0){ + cuda_set_device(net->gpu_index); } #endif fprintf(stderr, "Saving weights to %s\n", filename); @@ -830,17 +1016,18 @@ void save_weights_upto(network net, char *filename, int cutoff) if(!fp) file_error(filename); int major = 0; - int minor = 1; + int minor = 2; int revision = 0; fwrite(&major, sizeof(int), 1, fp); fwrite(&minor, sizeof(int), 1, fp); fwrite(&revision, sizeof(int), 1, fp); - fwrite(net.seen, sizeof(int), 1, fp); + fwrite(net->seen, sizeof(size_t), 1, fp); int i; - for(i = 0; i < net.n && i < cutoff; ++i){ - layer l = net.layers[i]; - if(l.type == CONVOLUTIONAL){ + for(i = 0; i < net->n && i < cutoff; ++i){ + layer l = net->layers[i]; + if (l.dontsave) continue; + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ save_convolutional_weights(l, fp); } if(l.type == CONNECTED){ save_connected_weights(l, fp); @@ -850,14 +1037,29 @@ void save_weights_upto(network net, char *filename, int cutoff) save_connected_weights(*(l.input_layer), fp); save_connected_weights(*(l.self_layer), fp); save_connected_weights(*(l.output_layer), fp); - } if(l.type == GRU){ - save_connected_weights(*(l.input_z_layer), fp); - save_connected_weights(*(l.input_r_layer), fp); - save_connected_weights(*(l.input_h_layer), fp); - save_connected_weights(*(l.state_z_layer), fp); - save_connected_weights(*(l.state_r_layer), fp); - save_connected_weights(*(l.state_h_layer), fp); - } if(l.type == CRNN){ + } if (l.type == LSTM) { + save_connected_weights(*(l.wi), fp); + save_connected_weights(*(l.wf), fp); + save_connected_weights(*(l.wo), fp); + save_connected_weights(*(l.wg), fp); + save_connected_weights(*(l.ui), fp); + save_connected_weights(*(l.uf), fp); + save_connected_weights(*(l.uo), fp); + save_connected_weights(*(l.ug), fp); + } if (l.type == GRU) { + if(1){ + save_connected_weights(*(l.wz), fp); + save_connected_weights(*(l.wr), fp); + save_connected_weights(*(l.wh), fp); + save_connected_weights(*(l.uz), fp); + save_connected_weights(*(l.ur), fp); + save_connected_weights(*(l.uh), fp); + }else{ + save_connected_weights(*(l.reset_layer), fp); + save_connected_weights(*(l.update_layer), fp); + save_connected_weights(*(l.state_layer), fp); + } + } if(l.type == CRNN){ save_convolutional_weights(*(l.input_layer), fp); save_convolutional_weights(*(l.self_layer), fp); save_convolutional_weights(*(l.output_layer), fp); @@ -875,9 +1077,9 @@ void save_weights_upto(network net, char *filename, int cutoff) } fclose(fp); } -void save_weights(network net, char *filename) +void save_weights(network *net, char *filename) { - save_weights_upto(net, filename, net.n); + save_weights_upto(net, filename, net->n); } void transpose_matrix(float *a, int rows, int cols) @@ -965,7 +1167,8 @@ void load_convolutional_weights(layer l, FILE *fp) //load_convolutional_weights_binary(l, fp); //return; } - int num = l.n*l.c*l.size*l.size; + if(l.numload) l.n = l.numload; + int num = l.c/l.groups*l.n*l.size*l.size; fread(l.biases, sizeof(float), l.n, fp); if (l.batch_normalize && (!l.dontloadscales)){ fread(l.scales, sizeof(float), l.n, fp); @@ -986,12 +1189,19 @@ void load_convolutional_weights(layer l, FILE *fp) fill_cpu(l.n, 0, l.rolling_mean, 1); fill_cpu(l.n, 0, l.rolling_variance, 1); } + if(0){ + int i; + for(i = 0; i < l.n; ++i){ + printf("%g, ", l.rolling_mean[i]); + } + printf("\n"); + for(i = 0; i < l.n; ++i){ + printf("%g, ", l.rolling_variance[i]); + } + printf("\n"); + } } fread(l.weights, sizeof(float), num, fp); - if(l.adam){ - fread(l.m, sizeof(float), num, fp); - fread(l.v, sizeof(float), num, fp); - } //if(l.c == 3) scal_cpu(num, 1./256, l.weights, 1); if (l.flipped) { transpose_matrix(l.weights, l.c*l.size*l.size, l.n); @@ -1005,7 +1215,7 @@ void load_convolutional_weights(layer l, FILE *fp) } -void load_weights_upto(network *net, char *filename, int cutoff) +void load_weights_upto(network *net, char *filename, int start, int cutoff) { #ifdef GPU if(net->gpu_index >= 0){ @@ -1023,14 +1233,20 @@ void load_weights_upto(network *net, char *filename, int cutoff) fread(&major, sizeof(int), 1, fp); fread(&minor, sizeof(int), 1, fp); fread(&revision, sizeof(int), 1, fp); - fread(net->seen, sizeof(int), 1, fp); + if ((major*10 + minor) >= 2 && major < 1000 && minor < 1000){ + fread(net->seen, sizeof(size_t), 1, fp); + } else { + int iseen = 0; + fread(&iseen, sizeof(int), 1, fp); + *net->seen = iseen; + } int transpose = (major > 1000) || (minor > 1000); int i; - for(i = 0; i < net->n && i < cutoff; ++i){ + for(i = start; i < net->n && i < cutoff; ++i){ layer l = net->layers[i]; if (l.dontload) continue; - if(l.type == CONVOLUTIONAL){ + if(l.type == CONVOLUTIONAL || l.type == DECONVOLUTIONAL){ load_convolutional_weights(l, fp); } if(l.type == CONNECTED){ @@ -1049,13 +1265,29 @@ void load_weights_upto(network *net, char *filename, int cutoff) load_connected_weights(*(l.self_layer), fp, transpose); load_connected_weights(*(l.output_layer), fp, transpose); } - if(l.type == GRU){ - load_connected_weights(*(l.input_z_layer), fp, transpose); - load_connected_weights(*(l.input_r_layer), fp, transpose); - load_connected_weights(*(l.input_h_layer), fp, transpose); - load_connected_weights(*(l.state_z_layer), fp, transpose); - load_connected_weights(*(l.state_r_layer), fp, transpose); - load_connected_weights(*(l.state_h_layer), fp, transpose); + if (l.type == LSTM) { + load_connected_weights(*(l.wi), fp, transpose); + load_connected_weights(*(l.wf), fp, transpose); + load_connected_weights(*(l.wo), fp, transpose); + load_connected_weights(*(l.wg), fp, transpose); + load_connected_weights(*(l.ui), fp, transpose); + load_connected_weights(*(l.uf), fp, transpose); + load_connected_weights(*(l.uo), fp, transpose); + load_connected_weights(*(l.ug), fp, transpose); + } + if (l.type == GRU) { + if(1){ + load_connected_weights(*(l.wz), fp, transpose); + load_connected_weights(*(l.wr), fp, transpose); + load_connected_weights(*(l.wh), fp, transpose); + load_connected_weights(*(l.uz), fp, transpose); + load_connected_weights(*(l.ur), fp, transpose); + load_connected_weights(*(l.uh), fp, transpose); + }else{ + load_connected_weights(*(l.reset_layer), fp, transpose); + load_connected_weights(*(l.update_layer), fp, transpose); + load_connected_weights(*(l.state_layer), fp, transpose); + } } if(l.type == LOCAL){ int locations = l.out_w*l.out_h; @@ -1075,6 +1307,6 @@ void load_weights_upto(network *net, char *filename, int cutoff) void load_weights(network *net, char *filename) { - load_weights_upto(net, filename, net->n); + load_weights_upto(net, filename, 0, net->n); } diff --git a/image.darknet/inst/include/darknet/src/parser.h b/image.darknet/inst/include/darknet/src/parser.h index 6cff4fb..81aef2c 100644 --- a/image.darknet/inst/include/darknet/src/parser.h +++ b/image.darknet/inst/include/darknet/src/parser.h @@ -1,13 +1,9 @@ #ifndef PARSER_H #define PARSER_H +#include "darknet.h" #include "network.h" -network parse_network_cfg(char *filename); void save_network(network net, char *filename); -void save_weights(network net, char *filename); -void save_weights_upto(network net, char *filename, int cutoff); void save_weights_double(network net, char *filename); -void load_weights(network *net, char *filename); -void load_weights_upto(network *net, char *filename, int cutoff); #endif diff --git a/image.darknet/inst/include/darknet/src/region_layer.c b/image.darknet/inst/include/darknet/src/region_layer.c index f5522c3..179f5e3 100644 --- a/image.darknet/inst/include/darknet/src/region_layer.c +++ b/image.darknet/inst/include/darknet/src/region_layer.c @@ -4,6 +4,7 @@ #include "box.h" #include "cuda.h" #include "utils.h" + #include #include #include @@ -18,6 +19,10 @@ layer make_region_layer(int batch, int w, int h, int n, int classes, int coords) l.batch = batch; l.h = h; l.w = w; + l.c = n*(classes + coords + 1); + l.out_w = l.w; + l.out_h = l.h; + l.out_c = l.c; l.classes = classes; l.coords = coords; l.cost = calloc(1, sizeof(float)); @@ -25,7 +30,7 @@ layer make_region_layer(int batch, int w, int h, int n, int classes, int coords) l.bias_updates = calloc(n*2, sizeof(float)); l.outputs = h*w*n*(classes + coords + 1); l.inputs = l.outputs; - l.truths = 30*(5); + l.truths = 30*(l.coords + 1); l.delta = calloc(batch*l.outputs, sizeof(float)); l.output = calloc(batch*l.outputs, sizeof(float)); int i; @@ -68,19 +73,19 @@ void resize_region_layer(layer *l, int w, int h) #endif } -box get_region_box(float *x, float *biases, int n, int index, int i, int j, int w, int h) +box get_region_box(float *x, float *biases, int n, int index, int i, int j, int w, int h, int stride) { box b; - b.x = (i + logistic_activate(x[index + 0])) / w; - b.y = (j + logistic_activate(x[index + 1])) / h; - b.w = exp(x[index + 2]) * biases[2*n] / w; - b.h = exp(x[index + 3]) * biases[2*n+1] / h; + b.x = (i + x[index + 0*stride]) / w; + b.y = (j + x[index + 1*stride]) / h; + b.w = exp(x[index + 2*stride]) * biases[2*n] / w; + b.h = exp(x[index + 3*stride]) * biases[2*n+1] / h; return b; } -float delta_region_box(box truth, float *x, float *biases, int n, int index, int i, int j, int w, int h, float *delta, float scale) +float delta_region_box(box truth, float *x, float *biases, int n, int index, int i, int j, int w, int h, float *delta, float scale, int stride) { - box pred = get_region_box(x, biases, n, index, i, j, w, h); + box pred = get_region_box(x, biases, n, index, i, j, w, h, stride); float iou = box_iou(pred, truth); float tx = (truth.x*w - i); @@ -88,34 +93,47 @@ float delta_region_box(box truth, float *x, float *biases, int n, int index, int float tw = log(truth.w*w / biases[2*n]); float th = log(truth.h*h / biases[2*n + 1]); - delta[index + 0] = scale * (tx - logistic_activate(x[index + 0])) * logistic_gradient(logistic_activate(x[index + 0])); - delta[index + 1] = scale * (ty - logistic_activate(x[index + 1])) * logistic_gradient(logistic_activate(x[index + 1])); - delta[index + 2] = scale * (tw - x[index + 2]); - delta[index + 3] = scale * (th - x[index + 3]); + delta[index + 0*stride] = scale * (tx - x[index + 0*stride]); + delta[index + 1*stride] = scale * (ty - x[index + 1*stride]); + delta[index + 2*stride] = scale * (tw - x[index + 2*stride]); + delta[index + 3*stride] = scale * (th - x[index + 3*stride]); return iou; } -void delta_region_class(float *output, float *delta, int index, int class, int classes, tree *hier, float scale, float *avg_cat) +void delta_region_mask(float *truth, float *x, int n, int index, float *delta, int stride, int scale) +{ + int i; + for(i = 0; i < n; ++i){ + delta[index + i*stride] = scale*(truth[i] - x[index + i*stride]); + } +} + + +void delta_region_class(float *output, float *delta, int index, int class, int classes, tree *hier, float scale, int stride, float *avg_cat, int tag) { int i, n; if(hier){ float pred = 1; while(class >= 0){ - pred *= output[index + class]; + pred *= output[index + stride*class]; int g = hier->group[class]; int offset = hier->group_offset[g]; for(i = 0; i < hier->group_size[g]; ++i){ - delta[index + offset + i] = scale * (0 - output[index + offset + i]); + delta[index + stride*(offset + i)] = scale * (0 - output[index + stride*(offset + i)]); } - delta[index + class] = scale * (1 - output[index + class]); + delta[index + stride*class] = scale * (1 - output[index + stride*class]); class = hier->parent[class]; } *avg_cat += pred; } else { + if (delta[index] && tag){ + delta[index + stride*class] = scale * (1 - output[index + stride*class]); + return; + } for(n = 0; n < classes; ++n){ - delta[index + n] = scale * (((n == class)?1 : 0) - output[index + n]); - if(n == class) *avg_cat += output[index + n]; + delta[index + stride*n] = scale * (((n == class)?1 : 0) - output[index + stride*n]); + if(n == class) *avg_cat += output[index + stride*n]; } } } @@ -130,42 +148,45 @@ float tisnan(float x) return (x != x); } -void softmax_tree(float *input, int batch, int inputs, float temp, tree *hierarchy, float *output); -void forward_region_layer(const layer l, network_state state) +int entry_index(layer l, int batch, int location, int entry) +{ + int n = location / (l.w*l.h); + int loc = location % (l.w*l.h); + return batch*l.outputs + n*l.w*l.h*(l.coords+l.classes+1) + entry*l.w*l.h + loc; +} + +void forward_region_layer(const layer l, network net) { int i,j,b,t,n; - int size = l.coords + l.classes + 1; - memcpy(l.output, state.input, l.outputs*l.batch*sizeof(float)); + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); + #ifndef GPU - flatten(l.output, l.w*l.h, size*l.n, l.batch, 1); -#endif for (b = 0; b < l.batch; ++b){ - for(i = 0; i < l.h*l.w*l.n; ++i){ - int index = size*i + b*l.outputs; - l.output[index + 4] = logistic_activate(l.output[index + 4]); + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + activate_array(l.output + index, 2*l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, l.coords); + if(!l.background) activate_array(l.output + index, l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, l.coords + 1); + if(!l.softmax && !l.softmax_tree) activate_array(l.output + index, l.classes*l.w*l.h, LOGISTIC); } } - - -#ifndef GPU if (l.softmax_tree){ - for (b = 0; b < l.batch; ++b){ - for(i = 0; i < l.h*l.w*l.n; ++i){ - int index = size*i + b*l.outputs; - softmax_tree(l.output + index + 5, 1, 0, 1, l.softmax_tree, l.output + index + 5); - } + int i; + int count = l.coords + 1; + for (i = 0; i < l.softmax_tree->groups; ++i) { + int group_size = l.softmax_tree->group_size[i]; + softmax_cpu(net.input + count, group_size, l.batch, l.inputs, l.n*l.w*l.h, 1, l.n*l.w*l.h, l.temperature, l.output + count); + count += group_size; } } else if (l.softmax){ - for (b = 0; b < l.batch; ++b){ - for(i = 0; i < l.h*l.w*l.n; ++i){ - int index = size*i + b*l.outputs; - softmax(l.output + index + 5, l.classes, 1, l.output + index + 5); - } - } + int index = entry_index(l, 0, 0, l.coords + !l.background); + softmax_cpu(net.input + index, l.classes + l.background, l.batch*l.n, l.inputs/l.n, l.w*l.h, 1, l.w*l.h, 1, l.output + index); } #endif - if(!state.train) return; + memset(l.delta, 0, l.outputs * l.batch * sizeof(float)); + if(!net.train) return; float avg_iou = 0; float recall = 0; float avg_cat = 0; @@ -178,26 +199,29 @@ void forward_region_layer(const layer l, network_state state) if(l.softmax_tree){ int onlyclass = 0; for(t = 0; t < 30; ++t){ - box truth = float_to_box(state.truth + t*5 + b*l.truths); + box truth = float_to_box(net.truth + t*(l.coords + 1) + b*l.truths, 1); if(!truth.x) break; - int class = state.truth[t*5 + b*l.truths + 4]; + int class = net.truth[t*(l.coords + 1) + b*l.truths + l.coords]; float maxp = 0; int maxi = 0; if(truth.x > 100000 && truth.y > 100000){ for(n = 0; n < l.n*l.w*l.h; ++n){ - int index = size*n + b*l.outputs + 5; - float scale = l.output[index-1]; - l.delta[index - 1] = l.noobject_scale * ((0 - l.output[index - 1]) * logistic_gradient(l.output[index - 1])); - float p = scale*get_hierarchy_probability(l.output + index, l.softmax_tree, class); + int class_index = entry_index(l, b, n, l.coords + 1); + int obj_index = entry_index(l, b, n, l.coords); + float scale = l.output[obj_index]; + l.delta[obj_index] = l.noobject_scale * (0 - l.output[obj_index]); + float p = scale*get_hierarchy_probability(l.output + class_index, l.softmax_tree, class, l.w*l.h); if(p > maxp){ maxp = p; maxi = n; } } - int index = size*maxi + b*l.outputs + 5; - delta_region_class(l.output, l.delta, index, class, l.classes, l.softmax_tree, l.class_scale, &avg_cat); - if(l.output[index - 1] < .3) l.delta[index - 1] = l.object_scale * ((.3 - l.output[index - 1]) * logistic_gradient(l.output[index - 1])); - else l.delta[index - 1] = 0; + int class_index = entry_index(l, b, maxi, l.coords + 1); + int obj_index = entry_index(l, b, maxi, l.coords); + delta_region_class(l.output, l.delta, class_index, class, l.classes, l.softmax_tree, l.class_scale, l.w*l.h, &avg_cat, !l.softmax); + if(l.output[obj_index] < .3) l.delta[obj_index] = l.object_scale * (.3 - l.output[obj_index]); + else l.delta[obj_index] = 0; + l.delta[obj_index] = 0; ++class_count; onlyclass = 1; break; @@ -208,190 +232,276 @@ void forward_region_layer(const layer l, network_state state) for (j = 0; j < l.h; ++j) { for (i = 0; i < l.w; ++i) { for (n = 0; n < l.n; ++n) { - int index = size*(j*l.w*l.n + i*l.n + n) + b*l.outputs; - box pred = get_region_box(l.output, l.biases, n, index, i, j, l.w, l.h); + int box_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 0); + box pred = get_region_box(l.output, l.biases, n, box_index, i, j, l.w, l.h, l.w*l.h); float best_iou = 0; for(t = 0; t < 30; ++t){ - box truth = float_to_box(state.truth + t*5 + b*l.truths); + box truth = float_to_box(net.truth + t*(l.coords + 1) + b*l.truths, 1); if(!truth.x) break; float iou = box_iou(pred, truth); if (iou > best_iou) { best_iou = iou; } } - avg_anyobj += l.output[index + 4]; - l.delta[index + 4] = l.noobject_scale * ((0 - l.output[index + 4]) * logistic_gradient(l.output[index + 4])); + int obj_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, l.coords); + avg_anyobj += l.output[obj_index]; + l.delta[obj_index] = l.noobject_scale * (0 - l.output[obj_index]); + if(l.background) l.delta[obj_index] = l.noobject_scale * (1 - l.output[obj_index]); if (best_iou > l.thresh) { - l.delta[index + 4] = 0; + l.delta[obj_index] = 0; } - if(*(state.net.seen) < 12800){ + if(*(net.seen) < 12800){ box truth = {0}; truth.x = (i + .5)/l.w; truth.y = (j + .5)/l.h; truth.w = l.biases[2*n]/l.w; truth.h = l.biases[2*n+1]/l.h; - delta_region_box(truth, l.output, l.biases, n, index, i, j, l.w, l.h, l.delta, .01); + delta_region_box(truth, l.output, l.biases, n, box_index, i, j, l.w, l.h, l.delta, .01, l.w*l.h); } } } } for(t = 0; t < 30; ++t){ - box truth = float_to_box(state.truth + t*5 + b*l.truths); + box truth = float_to_box(net.truth + t*(l.coords + 1) + b*l.truths, 1); if(!truth.x) break; float best_iou = 0; - int best_index = 0; int best_n = 0; i = (truth.x * l.w); j = (truth.y * l.h); - //printf("%d %f %d %f\n", i, truth.x*l.w, j, truth.y*l.h); box truth_shift = truth; truth_shift.x = 0; truth_shift.y = 0; - //printf("index %d %d\n",i, j); for(n = 0; n < l.n; ++n){ - int index = size*(j*l.w*l.n + i*l.n + n) + b*l.outputs; - box pred = get_region_box(l.output, l.biases, n, index, i, j, l.w, l.h); + int box_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 0); + box pred = get_region_box(l.output, l.biases, n, box_index, i, j, l.w, l.h, l.w*l.h); if(l.bias_match){ pred.w = l.biases[2*n]/l.w; pred.h = l.biases[2*n+1]/l.h; } - //printf("pred: (%f, %f) %f x %f\n", pred.x, pred.y, pred.w, pred.h); pred.x = 0; pred.y = 0; float iou = box_iou(pred, truth_shift); if (iou > best_iou){ - best_index = index; best_iou = iou; best_n = n; } } - //printf("%d %f (%f, %f) %f x %f\n", best_n, best_iou, truth.x, truth.y, truth.w, truth.h); - float iou = delta_region_box(truth, l.output, l.biases, best_n, best_index, i, j, l.w, l.h, l.delta, l.coord_scale); + int box_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, 0); + float iou = delta_region_box(truth, l.output, l.biases, best_n, box_index, i, j, l.w, l.h, l.delta, l.coord_scale * (2 - truth.w*truth.h), l.w*l.h); + if(l.coords > 4){ + int mask_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, 4); + delta_region_mask(net.truth + t*(l.coords + 1) + b*l.truths + 5, l.output, l.coords - 4, mask_index, l.delta, l.w*l.h, l.mask_scale); + } if(iou > .5) recall += 1; avg_iou += iou; - //l.delta[best_index + 4] = iou - l.output[best_index + 4]; - avg_obj += l.output[best_index + 4]; - l.delta[best_index + 4] = l.object_scale * (1 - l.output[best_index + 4]) * logistic_gradient(l.output[best_index + 4]); + int obj_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, l.coords); + avg_obj += l.output[obj_index]; + l.delta[obj_index] = l.object_scale * (1 - l.output[obj_index]); if (l.rescore) { - l.delta[best_index + 4] = l.object_scale * (iou - l.output[best_index + 4]) * logistic_gradient(l.output[best_index + 4]); + l.delta[obj_index] = l.object_scale * (iou - l.output[obj_index]); + } + if(l.background){ + l.delta[obj_index] = l.object_scale * (0 - l.output[obj_index]); } - - int class = state.truth[t*5 + b*l.truths + 4]; + int class = net.truth[t*(l.coords + 1) + b*l.truths + l.coords]; if (l.map) class = l.map[class]; - delta_region_class(l.output, l.delta, best_index + 5, class, l.classes, l.softmax_tree, l.class_scale, &avg_cat); + int class_index = entry_index(l, b, best_n*l.w*l.h + j*l.w + i, l.coords + 1); + delta_region_class(l.output, l.delta, class_index, class, l.classes, l.softmax_tree, l.class_scale, l.w*l.h, &avg_cat, !l.softmax); ++count; ++class_count; } } - //printf("\n"); -#ifndef GPU - flatten(l.delta, l.w*l.h, size*l.n, l.batch, 0); -#endif *(l.cost) = pow(mag_array(l.delta, l.outputs * l.batch), 2); printf("Region Avg IOU: %f, Class: %f, Obj: %f, No Obj: %f, Avg Recall: %f, count: %d\n", avg_iou/count, avg_cat/class_count, avg_obj/count, avg_anyobj/(l.w*l.h*l.n*l.batch), recall/count, count); } -void backward_region_layer(const layer l, network_state state) +void backward_region_layer(const layer l, network net) { - axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, state.delta, 1); + /* + int b; + int size = l.coords + l.classes + 1; + for (b = 0; b < l.batch*l.n; ++b){ + int index = (b*size + 4)*l.w*l.h; + gradient_array(l.output + index, l.w*l.h, LOGISTIC, l.delta + index); + } + axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, net.delta, 1); + */ +} + +void correct_region_boxes(detection *dets, int n, int w, int h, int netw, int neth, int relative) +{ + int i; + int new_w=0; + int new_h=0; + if (((float)netw/w) < ((float)neth/h)) { + new_w = netw; + new_h = (h * netw)/w; + } else { + new_h = neth; + new_w = (w * neth)/h; + } + for (i = 0; i < n; ++i){ + box b = dets[i].bbox; + b.x = (b.x - (netw - new_w)/2./netw) / ((float)new_w/netw); + b.y = (b.y - (neth - new_h)/2./neth) / ((float)new_h/neth); + b.w *= (float)netw/new_w; + b.h *= (float)neth/new_h; + if(!relative){ + b.x *= w; + b.w *= w; + b.y *= h; + b.h *= h; + } + dets[i].bbox = b; + } } -void get_region_boxes(layer l, int w, int h, float thresh, float **probs, box *boxes, int only_objectness, int *map, float tree_thresh) +void get_region_detections(layer l, int w, int h, int netw, int neth, float thresh, int *map, float tree_thresh, int relative, detection *dets) { - int i,j,n; + int i,j,n,z; float *predictions = l.output; + if (l.batch == 2) { + float *flip = l.output + l.outputs; + for (j = 0; j < l.h; ++j) { + for (i = 0; i < l.w/2; ++i) { + for (n = 0; n < l.n; ++n) { + for(z = 0; z < l.classes + l.coords + 1; ++z){ + int i1 = z*l.w*l.h*l.n + n*l.w*l.h + j*l.w + i; + int i2 = z*l.w*l.h*l.n + n*l.w*l.h + j*l.w + (l.w - i - 1); + float swap = flip[i1]; + flip[i1] = flip[i2]; + flip[i2] = swap; + if(z == 0){ + flip[i1] = -flip[i1]; + flip[i2] = -flip[i2]; + } + } + } + } + } + for(i = 0; i < l.outputs; ++i){ + l.output[i] = (l.output[i] + flip[i])/2.; + } + } for (i = 0; i < l.w*l.h; ++i){ int row = i / l.w; int col = i % l.w; for(n = 0; n < l.n; ++n){ - int index = i*l.n + n; - int p_index = index * (l.classes + 5) + 4; - float scale = predictions[p_index]; - int box_index = index * (l.classes + 5); - boxes[index] = get_region_box(predictions, l.biases, n, box_index, col, row, l.w, l.h); - boxes[index].x *= w; - boxes[index].y *= h; - boxes[index].w *= w; - boxes[index].h *= h; - - int class_index = index * (l.classes + 5) + 5; + int index = n*l.w*l.h + i; + for(j = 0; j < l.classes; ++j){ + dets[index].prob[j] = 0; + } + int obj_index = entry_index(l, 0, n*l.w*l.h + i, l.coords); + int box_index = entry_index(l, 0, n*l.w*l.h + i, 0); + int mask_index = entry_index(l, 0, n*l.w*l.h + i, 4); + float scale = l.background ? 1 : predictions[obj_index]; + dets[index].bbox = get_region_box(predictions, l.biases, n, box_index, col, row, l.w, l.h, l.w*l.h); + dets[index].objectness = scale > thresh ? scale : 0; + if(dets[index].mask){ + for(j = 0; j < l.coords - 4; ++j){ + dets[index].mask[j] = l.output[mask_index + j*l.w*l.h]; + } + } + + int class_index = entry_index(l, 0, n*l.w*l.h + i, l.coords + !l.background); if(l.softmax_tree){ - hierarchy_predictions(predictions + class_index, l.classes, l.softmax_tree, 0); + hierarchy_predictions(predictions + class_index, l.classes, l.softmax_tree, 0, l.w*l.h); if(map){ for(j = 0; j < 200; ++j){ - float prob = scale*predictions[class_index+map[j]]; - probs[index][j] = (prob > thresh) ? prob : 0; + int class_index = entry_index(l, 0, n*l.w*l.h + i, l.coords + 1 + map[j]); + float prob = scale*predictions[class_index]; + dets[index].prob[j] = (prob > thresh) ? prob : 0; } } else { - int j = hierarchy_top_prediction(predictions + class_index, l.softmax_tree, tree_thresh); - probs[index][j] = (scale > thresh) ? scale : 0; - probs[index][l.classes] = scale; + int j = hierarchy_top_prediction(predictions + class_index, l.softmax_tree, tree_thresh, l.w*l.h); + dets[index].prob[j] = (scale > thresh) ? scale : 0; } } else { - for(j = 0; j < l.classes; ++j){ - float prob = scale*predictions[class_index+j]; - probs[index][j] = (prob > thresh) ? prob : 0; + if(dets[index].objectness){ + for(j = 0; j < l.classes; ++j){ + int class_index = entry_index(l, 0, n*l.w*l.h + i, l.coords + 1 + j); + float prob = scale*predictions[class_index]; + dets[index].prob[j] = (prob > thresh) ? prob : 0; + } } } - if(only_objectness){ - probs[index][0] = scale; - } } } + correct_region_boxes(dets, l.w*l.h*l.n, w, h, netw, neth, relative); } #ifdef GPU -void forward_region_layer_gpu(const layer l, network_state state) +void forward_region_layer_gpu(const layer l, network net) { - /* - if(!state.train){ - copy_ongpu(l.batch*l.inputs, state.input, 1, l.output_gpu, 1); - return; - } - */ - flatten_ongpu(state.input, l.h*l.w, l.n*(l.coords + l.classes + 1), l.batch, 1, l.output_gpu); - if(l.softmax_tree){ - int i; - int count = 5; - for (i = 0; i < l.softmax_tree->groups; ++i) { - int group_size = l.softmax_tree->group_size[i]; - softmax_gpu(l.output_gpu+count, group_size, l.classes + 5, l.w*l.h*l.n*l.batch, 1, l.output_gpu + count); - count += group_size; + copy_gpu(l.batch*l.inputs, net.input_gpu, 1, l.output_gpu, 1); + int b, n; + for (b = 0; b < l.batch; ++b){ + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + activate_array_gpu(l.output_gpu + index, 2*l.w*l.h, LOGISTIC); + if(l.coords > 4){ + index = entry_index(l, b, n*l.w*l.h, 4); + activate_array_gpu(l.output_gpu + index, (l.coords - 4)*l.w*l.h, LOGISTIC); + } + index = entry_index(l, b, n*l.w*l.h, l.coords); + if(!l.background) activate_array_gpu(l.output_gpu + index, l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, l.coords + 1); + if(!l.softmax && !l.softmax_tree) activate_array_gpu(l.output_gpu + index, l.classes*l.w*l.h, LOGISTIC); } - }else if (l.softmax){ - softmax_gpu(l.output_gpu+5, l.classes, l.classes + 5, l.w*l.h*l.n*l.batch, 1, l.output_gpu + 5); } - - float *in_cpu = calloc(l.batch*l.inputs, sizeof(float)); - float *truth_cpu = 0; - if(state.truth){ - int num_truth = l.batch*l.truths; - truth_cpu = calloc(num_truth, sizeof(float)); - cuda_pull_array(state.truth, truth_cpu, num_truth); + if (l.softmax_tree){ + int index = entry_index(l, 0, 0, l.coords + 1); + softmax_tree(net.input_gpu + index, l.w*l.h, l.batch*l.n, l.inputs/l.n, 1, l.output_gpu + index, *l.softmax_tree); + } else if (l.softmax) { + int index = entry_index(l, 0, 0, l.coords + !l.background); + softmax_gpu(net.input_gpu + index, l.classes + l.background, l.batch*l.n, l.inputs/l.n, l.w*l.h, 1, l.w*l.h, 1, l.output_gpu + index); } - cuda_pull_array(l.output_gpu, in_cpu, l.batch*l.inputs); - network_state cpu_state = state; - cpu_state.train = state.train; - cpu_state.truth = truth_cpu; - cpu_state.input = in_cpu; - forward_region_layer(l, cpu_state); + if(!net.train || l.onlyforward){ + cuda_pull_array(l.output_gpu, l.output, l.batch*l.outputs); + return; + } + + cuda_pull_array(l.output_gpu, net.input, l.batch*l.inputs); + forward_region_layer(l, net); //cuda_push_array(l.output_gpu, l.output, l.batch*l.outputs); - free(cpu_state.input); - if(!state.train) return; + if(!net.train) return; cuda_push_array(l.delta_gpu, l.delta, l.batch*l.outputs); - if(cpu_state.truth) free(cpu_state.truth); } -void backward_region_layer_gpu(layer l, network_state state) +void backward_region_layer_gpu(const layer l, network net) { - flatten_ongpu(l.delta_gpu, l.h*l.w, l.n*(l.coords + l.classes + 1), l.batch, 0, state.delta); + int b, n; + for (b = 0; b < l.batch; ++b){ + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + gradient_array_gpu(l.output_gpu + index, 2*l.w*l.h, LOGISTIC, l.delta_gpu + index); + if(l.coords > 4){ + index = entry_index(l, b, n*l.w*l.h, 4); + gradient_array_gpu(l.output_gpu + index, (l.coords - 4)*l.w*l.h, LOGISTIC, l.delta_gpu + index); + } + index = entry_index(l, b, n*l.w*l.h, l.coords); + if(!l.background) gradient_array_gpu(l.output_gpu + index, l.w*l.h, LOGISTIC, l.delta_gpu + index); + } + } + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); } #endif +void zero_objectness(layer l) +{ + int i, n; + for (i = 0; i < l.w*l.h; ++i){ + for(n = 0; n < l.n; ++n){ + int obj_index = entry_index(l, 0, n*l.w*l.h + i, l.coords); + l.output[obj_index] = 0; + } + } +} + diff --git a/image.darknet/inst/include/darknet/src/region_layer.h b/image.darknet/inst/include/darknet/src/region_layer.h index 9a3b7cd..9f12fd1 100644 --- a/image.darknet/inst/include/darknet/src/region_layer.h +++ b/image.darknet/inst/include/darknet/src/region_layer.h @@ -1,18 +1,18 @@ #ifndef REGION_LAYER_H #define REGION_LAYER_H +#include "darknet.h" #include "layer.h" #include "network.h" -layer make_region_layer(int batch, int h, int w, int n, int classes, int coords); -void forward_region_layer(const layer l, network_state state); -void backward_region_layer(const layer l, network_state state); -void get_region_boxes(layer l, int w, int h, float thresh, float **probs, box *boxes, int only_objectness, int *map, float tree_thresh); +layer make_region_layer(int batch, int w, int h, int n, int classes, int coords); +void forward_region_layer(const layer l, network net); +void backward_region_layer(const layer l, network net); void resize_region_layer(layer *l, int w, int h); #ifdef GPU -void forward_region_layer_gpu(const layer l, network_state state); -void backward_region_layer_gpu(layer l, network_state state); +void forward_region_layer_gpu(const layer l, network net); +void backward_region_layer_gpu(layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/reorg_layer.c b/image.darknet/inst/include/darknet/src/reorg_layer.c index 2abca8f..31d6b84 100644 --- a/image.darknet/inst/include/darknet/src/reorg_layer.c +++ b/image.darknet/inst/include/darknet/src/reorg_layer.c @@ -1,18 +1,21 @@ #include "reorg_layer.h" #include "cuda.h" #include "blas.h" + #include -layer make_reorg_layer(int batch, int w, int h, int c, int stride, int reverse) +layer make_reorg_layer(int batch, int w, int h, int c, int stride, int reverse, int flatten, int extra) { layer l = {0}; l.type = REORG; l.batch = batch; l.stride = stride; + l.extra = extra; l.h = h; l.w = w; l.c = c; + l.flatten = flatten; if(reverse){ l.out_w = w*stride; l.out_h = h*stride; @@ -23,10 +26,20 @@ layer make_reorg_layer(int batch, int w, int h, int c, int stride, int reverse) l.out_c = c*(stride*stride); } l.reverse = reverse; - fprintf(stderr, "reorg /%2d %4d x%4d x%4d -> %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c); + l.outputs = l.out_h * l.out_w * l.out_c; l.inputs = h*w*c; - int output_size = l.out_h * l.out_w * l.out_c * batch; + if(l.extra){ + l.out_w = l.out_h = l.out_c = 0; + l.outputs = l.inputs + l.extra; + } + + if(extra){ + fprintf(stderr, "reorg %4d -> %4d\n", l.inputs, l.outputs); + } else { + fprintf(stderr, "reorg /%2d %4d x%4d x%4d -> %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c); + } + int output_size = l.outputs * batch; l.output = calloc(output_size, sizeof(float)); l.delta = calloc(output_size, sizeof(float)); @@ -75,40 +88,86 @@ void resize_reorg_layer(layer *l, int w, int h) #endif } -void forward_reorg_layer(const layer l, network_state state) +void forward_reorg_layer(const layer l, network net) { - if(l.reverse){ - reorg_cpu(state.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.output); - }else { - reorg_cpu(state.input, l.w, l.h, l.c, l.batch, l.stride, 0, l.output); + int i; + if(l.flatten){ + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); + if(l.reverse){ + flatten(l.output, l.w*l.h, l.c, l.batch, 0); + }else{ + flatten(l.output, l.w*l.h, l.c, l.batch, 1); + } + } else if (l.extra) { + for(i = 0; i < l.batch; ++i){ + copy_cpu(l.inputs, net.input + i*l.inputs, 1, l.output + i*l.outputs, 1); + } + } else if (l.reverse){ + reorg_cpu(net.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.output); + } else { + reorg_cpu(net.input, l.w, l.h, l.c, l.batch, l.stride, 0, l.output); } } -void backward_reorg_layer(const layer l, network_state state) +void backward_reorg_layer(const layer l, network net) { - if(l.reverse){ - reorg_cpu(l.delta, l.w, l.h, l.c, l.batch, l.stride, 0, state.delta); + int i; + if(l.flatten){ + memcpy(net.delta, l.delta, l.outputs*l.batch*sizeof(float)); + if(l.reverse){ + flatten(net.delta, l.w*l.h, l.c, l.batch, 1); + }else{ + flatten(net.delta, l.w*l.h, l.c, l.batch, 0); + } + } else if(l.reverse){ + reorg_cpu(l.delta, l.w, l.h, l.c, l.batch, l.stride, 0, net.delta); + } else if (l.extra) { + for(i = 0; i < l.batch; ++i){ + copy_cpu(l.inputs, l.delta + i*l.outputs, 1, net.delta + i*l.inputs, 1); + } }else{ - reorg_cpu(l.delta, l.w, l.h, l.c, l.batch, l.stride, 1, state.delta); + reorg_cpu(l.delta, l.w, l.h, l.c, l.batch, l.stride, 1, net.delta); } } #ifdef GPU -void forward_reorg_layer_gpu(layer l, network_state state) +void forward_reorg_layer_gpu(layer l, network net) { - if(l.reverse){ - reorg_ongpu(state.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.output_gpu); + int i; + if(l.flatten){ + if(l.reverse){ + flatten_gpu(net.input_gpu, l.w*l.h, l.c, l.batch, 0, l.output_gpu); + }else{ + flatten_gpu(net.input_gpu, l.w*l.h, l.c, l.batch, 1, l.output_gpu); + } + } else if (l.extra) { + for(i = 0; i < l.batch; ++i){ + copy_gpu(l.inputs, net.input_gpu + i*l.inputs, 1, l.output_gpu + i*l.outputs, 1); + } + } else if (l.reverse) { + reorg_gpu(net.input_gpu, l.w, l.h, l.c, l.batch, l.stride, 1, l.output_gpu); }else { - reorg_ongpu(state.input, l.w, l.h, l.c, l.batch, l.stride, 0, l.output_gpu); + reorg_gpu(net.input_gpu, l.w, l.h, l.c, l.batch, l.stride, 0, l.output_gpu); } } -void backward_reorg_layer_gpu(layer l, network_state state) +void backward_reorg_layer_gpu(layer l, network net) { - if(l.reverse){ - reorg_ongpu(l.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 0, state.delta); - }else{ - reorg_ongpu(l.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 1, state.delta); + if(l.flatten){ + if(l.reverse){ + flatten_gpu(l.delta_gpu, l.w*l.h, l.c, l.batch, 1, net.delta_gpu); + }else{ + flatten_gpu(l.delta_gpu, l.w*l.h, l.c, l.batch, 0, net.delta_gpu); + } + } else if (l.extra) { + int i; + for(i = 0; i < l.batch; ++i){ + copy_gpu(l.inputs, l.delta_gpu + i*l.outputs, 1, net.delta_gpu + i*l.inputs, 1); + } + } else if(l.reverse){ + reorg_gpu(l.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 0, net.delta_gpu); + } else { + reorg_gpu(l.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 1, net.delta_gpu); } } #endif diff --git a/image.darknet/inst/include/darknet/src/reorg_layer.h b/image.darknet/inst/include/darknet/src/reorg_layer.h index 21c22cd..e6513a5 100644 --- a/image.darknet/inst/include/darknet/src/reorg_layer.h +++ b/image.darknet/inst/include/darknet/src/reorg_layer.h @@ -6,14 +6,14 @@ #include "layer.h" #include "network.h" -layer make_reorg_layer(int batch, int h, int w, int c, int stride, int reverse); +layer make_reorg_layer(int batch, int w, int h, int c, int stride, int reverse, int flatten, int extra); void resize_reorg_layer(layer *l, int w, int h); -void forward_reorg_layer(const layer l, network_state state); -void backward_reorg_layer(const layer l, network_state state); +void forward_reorg_layer(const layer l, network net); +void backward_reorg_layer(const layer l, network net); #ifdef GPU -void forward_reorg_layer_gpu(layer l, network_state state); -void backward_reorg_layer_gpu(layer l, network_state state); +void forward_reorg_layer_gpu(layer l, network net); +void backward_reorg_layer_gpu(layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/rnn_layer.c b/image.darknet/inst/include/darknet/src/rnn_layer.c index 83fda13..8c9b457 100644 --- a/image.darknet/inst/include/darknet/src/rnn_layer.c +++ b/image.darknet/inst/include/darknet/src/rnn_layer.c @@ -26,7 +26,7 @@ static void increment_layer(layer *l, int steps) #endif } -layer make_rnn_layer(int batch, int inputs, int hidden, int outputs, int steps, ACTIVATION activation, int batch_normalize, int log) +layer make_rnn_layer(int batch, int inputs, int outputs, int steps, ACTIVATION activation, int batch_normalize, int adam) { fprintf(stderr, "RNN Layer: %d inputs, %d outputs\n", inputs, outputs); batch = batch / steps; @@ -34,24 +34,24 @@ layer make_rnn_layer(int batch, int inputs, int hidden, int outputs, int steps, l.batch = batch; l.type = RNN; l.steps = steps; - l.hidden = hidden; l.inputs = inputs; - l.state = calloc(batch*hidden*(steps+1), sizeof(float)); + l.state = calloc(batch*outputs, sizeof(float)); + l.prev_state = calloc(batch*outputs, sizeof(float)); l.input_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.input_layer) = make_connected_layer(batch*steps, inputs, hidden, activation, batch_normalize); + *(l.input_layer) = make_connected_layer(batch*steps, inputs, outputs, activation, batch_normalize, adam); l.input_layer->batch = batch; l.self_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.self_layer) = make_connected_layer(batch*steps, hidden, hidden, (log==2)?LOGGY:(log==1?LOGISTIC:activation), batch_normalize); + *(l.self_layer) = make_connected_layer(batch*steps, outputs, outputs, activation, batch_normalize, adam); l.self_layer->batch = batch; l.output_layer = malloc(sizeof(layer)); fprintf(stderr, "\t\t"); - *(l.output_layer) = make_connected_layer(batch*steps, hidden, outputs, activation, batch_normalize); + *(l.output_layer) = make_connected_layer(batch*steps, outputs, outputs, activation, batch_normalize, adam); l.output_layer->batch = batch; l.outputs = outputs; @@ -65,66 +65,72 @@ layer make_rnn_layer(int batch, int inputs, int hidden, int outputs, int steps, l.forward_gpu = forward_rnn_layer_gpu; l.backward_gpu = backward_rnn_layer_gpu; l.update_gpu = update_rnn_layer_gpu; - l.state_gpu = cuda_make_array(l.state, batch*hidden*(steps+1)); + l.state_gpu = cuda_make_array(0, batch*outputs); + l.prev_state_gpu = cuda_make_array(0, batch*outputs); l.output_gpu = l.output_layer->output_gpu; l.delta_gpu = l.output_layer->delta_gpu; +#ifdef CUDNN + cudnnSetTensor4dDescriptor(l.input_layer->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.input_layer->out_c, l.input_layer->out_h, l.input_layer->out_w); + cudnnSetTensor4dDescriptor(l.self_layer->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.self_layer->out_c, l.self_layer->out_h, l.self_layer->out_w); + cudnnSetTensor4dDescriptor(l.output_layer->dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, batch, l.output_layer->out_c, l.output_layer->out_h, l.output_layer->out_w); +#endif #endif return l; } -void update_rnn_layer(layer l, int batch, float learning_rate, float momentum, float decay) +void update_rnn_layer(layer l, update_args a) { - update_connected_layer(*(l.input_layer), batch, learning_rate, momentum, decay); - update_connected_layer(*(l.self_layer), batch, learning_rate, momentum, decay); - update_connected_layer(*(l.output_layer), batch, learning_rate, momentum, decay); + update_connected_layer(*(l.input_layer), a); + update_connected_layer(*(l.self_layer), a); + update_connected_layer(*(l.output_layer), a); } -void forward_rnn_layer(layer l, network_state state) +void forward_rnn_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); layer output_layer = *(l.output_layer); fill_cpu(l.outputs * l.batch * l.steps, 0, output_layer.delta, 1); - fill_cpu(l.hidden * l.batch * l.steps, 0, self_layer.delta, 1); - fill_cpu(l.hidden * l.batch * l.steps, 0, input_layer.delta, 1); - if(state.train) fill_cpu(l.hidden * l.batch, 0, l.state, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, self_layer.delta, 1); + fill_cpu(l.outputs * l.batch * l.steps, 0, input_layer.delta, 1); + if(net.train) fill_cpu(l.outputs * l.batch, 0, l.state, 1); for (i = 0; i < l.steps; ++i) { - s.input = state.input; + s.input = net.input; forward_connected_layer(input_layer, s); s.input = l.state; forward_connected_layer(self_layer, s); float *old_state = l.state; - if(state.train) l.state += l.hidden*l.batch; + if(net.train) l.state += l.outputs*l.batch; if(l.shortcut){ - copy_cpu(l.hidden * l.batch, old_state, 1, l.state, 1); + copy_cpu(l.outputs * l.batch, old_state, 1, l.state, 1); }else{ - fill_cpu(l.hidden * l.batch, 0, l.state, 1); + fill_cpu(l.outputs * l.batch, 0, l.state, 1); } - axpy_cpu(l.hidden * l.batch, 1, input_layer.output, 1, l.state, 1); - axpy_cpu(l.hidden * l.batch, 1, self_layer.output, 1, l.state, 1); + axpy_cpu(l.outputs * l.batch, 1, input_layer.output, 1, l.state, 1); + axpy_cpu(l.outputs * l.batch, 1, self_layer.output, 1, l.state, 1); s.input = l.state; forward_connected_layer(output_layer, s); - state.input += l.inputs*l.batch; + net.input += l.inputs*l.batch; increment_layer(&input_layer, 1); increment_layer(&self_layer, 1); increment_layer(&output_layer, 1); } } -void backward_rnn_layer(layer l, network_state state) +void backward_rnn_layer(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = net; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -134,34 +140,34 @@ void backward_rnn_layer(layer l, network_state state) increment_layer(&self_layer, l.steps-1); increment_layer(&output_layer, l.steps-1); - l.state += l.hidden*l.batch*l.steps; + l.state += l.outputs*l.batch*l.steps; for (i = l.steps-1; i >= 0; --i) { - copy_cpu(l.hidden * l.batch, input_layer.output, 1, l.state, 1); - axpy_cpu(l.hidden * l.batch, 1, self_layer.output, 1, l.state, 1); + copy_cpu(l.outputs * l.batch, input_layer.output, 1, l.state, 1); + axpy_cpu(l.outputs * l.batch, 1, self_layer.output, 1, l.state, 1); s.input = l.state; s.delta = self_layer.delta; backward_connected_layer(output_layer, s); - l.state -= l.hidden*l.batch; + l.state -= l.outputs*l.batch; /* if(i > 0){ - copy_cpu(l.hidden * l.batch, input_layer.output - l.hidden*l.batch, 1, l.state, 1); - axpy_cpu(l.hidden * l.batch, 1, self_layer.output - l.hidden*l.batch, 1, l.state, 1); + copy_cpu(l.outputs * l.batch, input_layer.output - l.outputs*l.batch, 1, l.state, 1); + axpy_cpu(l.outputs * l.batch, 1, self_layer.output - l.outputs*l.batch, 1, l.state, 1); }else{ - fill_cpu(l.hidden * l.batch, 0, l.state, 1); + fill_cpu(l.outputs * l.batch, 0, l.state, 1); } */ s.input = l.state; - s.delta = self_layer.delta - l.hidden*l.batch; + s.delta = self_layer.delta - l.outputs*l.batch; if (i == 0) s.delta = 0; backward_connected_layer(self_layer, s); - copy_cpu(l.hidden*l.batch, self_layer.delta, 1, input_layer.delta, 1); - if (i > 0 && l.shortcut) axpy_cpu(l.hidden*l.batch, 1, self_layer.delta, 1, self_layer.delta - l.hidden*l.batch, 1); - s.input = state.input + i*l.inputs*l.batch; - if(state.delta) s.delta = state.delta + i*l.inputs*l.batch; + copy_cpu(l.outputs*l.batch, self_layer.delta, 1, input_layer.delta, 1); + if (i > 0 && l.shortcut) axpy_cpu(l.outputs*l.batch, 1, self_layer.delta, 1, self_layer.delta - l.outputs*l.batch, 1); + s.input = net.input + i*l.inputs*l.batch; + if(net.delta) s.delta = net.delta + i*l.inputs*l.batch; else s.delta = 0; backward_connected_layer(input_layer, s); @@ -187,58 +193,56 @@ void push_rnn_layer(layer l) push_connected_layer(*(l.output_layer)); } -void update_rnn_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay) +void update_rnn_layer_gpu(layer l, update_args a) { - update_connected_layer_gpu(*(l.input_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.self_layer), batch, learning_rate, momentum, decay); - update_connected_layer_gpu(*(l.output_layer), batch, learning_rate, momentum, decay); + update_connected_layer_gpu(*(l.input_layer), a); + update_connected_layer_gpu(*(l.self_layer), a); + update_connected_layer_gpu(*(l.output_layer), a); } -void forward_rnn_layer_gpu(layer l, network_state state) +void forward_rnn_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = {0}; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); layer output_layer = *(l.output_layer); - fill_ongpu(l.outputs * l.batch * l.steps, 0, output_layer.delta_gpu, 1); - fill_ongpu(l.hidden * l.batch * l.steps, 0, self_layer.delta_gpu, 1); - fill_ongpu(l.hidden * l.batch * l.steps, 0, input_layer.delta_gpu, 1); - if(state.train) fill_ongpu(l.hidden * l.batch, 0, l.state_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, output_layer.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, self_layer.delta_gpu, 1); + fill_gpu(l.outputs * l.batch * l.steps, 0, input_layer.delta_gpu, 1); + + if(net.train) { + fill_gpu(l.outputs * l.batch * l.steps, 0, l.delta_gpu, 1); + copy_gpu(l.outputs*l.batch, l.state_gpu, 1, l.prev_state_gpu, 1); + } for (i = 0; i < l.steps; ++i) { - s.input = state.input; + s.input_gpu = net.input_gpu; forward_connected_layer_gpu(input_layer, s); - s.input = l.state_gpu; + s.input_gpu = l.state_gpu; forward_connected_layer_gpu(self_layer, s); - float *old_state = l.state_gpu; - if(state.train) l.state_gpu += l.hidden*l.batch; - if(l.shortcut){ - copy_ongpu(l.hidden * l.batch, old_state, 1, l.state_gpu, 1); - }else{ - fill_ongpu(l.hidden * l.batch, 0, l.state_gpu, 1); - } - axpy_ongpu(l.hidden * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); - axpy_ongpu(l.hidden * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); + fill_gpu(l.outputs * l.batch, 0, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); - s.input = l.state_gpu; + s.input_gpu = l.state_gpu; forward_connected_layer_gpu(output_layer, s); - state.input += l.inputs*l.batch; + net.input_gpu += l.inputs*l.batch; increment_layer(&input_layer, 1); increment_layer(&self_layer, 1); increment_layer(&output_layer, 1); } } -void backward_rnn_layer_gpu(layer l, network_state state) +void backward_rnn_layer_gpu(layer l, network net) { - network_state s = {0}; - s.train = state.train; + network s = {0}; + s.train = net.train; int i; layer input_layer = *(l.input_layer); layer self_layer = *(l.self_layer); @@ -246,32 +250,43 @@ void backward_rnn_layer_gpu(layer l, network_state state) increment_layer(&input_layer, l.steps - 1); increment_layer(&self_layer, l.steps - 1); increment_layer(&output_layer, l.steps - 1); - l.state_gpu += l.hidden*l.batch*l.steps; + float *last_input = input_layer.output_gpu; + float *last_self = self_layer.output_gpu; for (i = l.steps-1; i >= 0; --i) { + fill_gpu(l.outputs * l.batch, 0, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, input_layer.output_gpu, 1, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, self_layer.output_gpu, 1, l.state_gpu, 1); - s.input = l.state_gpu; - s.delta = self_layer.delta_gpu; + s.input_gpu = l.state_gpu; + s.delta_gpu = self_layer.delta_gpu; backward_connected_layer_gpu(output_layer, s); - l.state_gpu -= l.hidden*l.batch; + if(i != 0) { + fill_gpu(l.outputs * l.batch, 0, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, input_layer.output_gpu - l.outputs*l.batch, 1, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, self_layer.output_gpu - l.outputs*l.batch, 1, l.state_gpu, 1); + }else { + copy_gpu(l.outputs*l.batch, l.prev_state_gpu, 1, l.state_gpu, 1); + } - copy_ongpu(l.hidden*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); + copy_gpu(l.outputs*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); - s.input = l.state_gpu; - s.delta = self_layer.delta_gpu - l.hidden*l.batch; - if (i == 0) s.delta = 0; + s.input_gpu = l.state_gpu; + s.delta_gpu = (i > 0) ? self_layer.delta_gpu - l.outputs*l.batch : 0; + if (i == 0) s.delta_gpu = 0; backward_connected_layer_gpu(self_layer, s); - //copy_ongpu(l.hidden*l.batch, self_layer.delta_gpu, 1, input_layer.delta_gpu, 1); - if (i > 0 && l.shortcut) axpy_ongpu(l.hidden*l.batch, 1, self_layer.delta_gpu, 1, self_layer.delta_gpu - l.hidden*l.batch, 1); - s.input = state.input + i*l.inputs*l.batch; - if(state.delta) s.delta = state.delta + i*l.inputs*l.batch; - else s.delta = 0; + s.input_gpu = net.input_gpu + i*l.inputs*l.batch; + if(net.delta_gpu) s.delta_gpu = net.delta_gpu + i*l.inputs*l.batch; + else s.delta_gpu = 0; backward_connected_layer_gpu(input_layer, s); increment_layer(&input_layer, -1); increment_layer(&self_layer, -1); increment_layer(&output_layer, -1); } + fill_gpu(l.outputs * l.batch, 0, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, last_input, 1, l.state_gpu, 1); + axpy_gpu(l.outputs * l.batch, 1, last_self, 1, l.state_gpu, 1); } #endif diff --git a/image.darknet/inst/include/darknet/src/rnn_layer.h b/image.darknet/inst/include/darknet/src/rnn_layer.h index bb9478b..270a63f 100644 --- a/image.darknet/inst/include/darknet/src/rnn_layer.h +++ b/image.darknet/inst/include/darknet/src/rnn_layer.h @@ -7,16 +7,16 @@ #include "network.h" #define USET -layer make_rnn_layer(int batch, int inputs, int hidden, int outputs, int steps, ACTIVATION activation, int batch_normalize, int log); +layer make_rnn_layer(int batch, int inputs, int outputs, int steps, ACTIVATION activation, int batch_normalize, int adam); -void forward_rnn_layer(layer l, network_state state); -void backward_rnn_layer(layer l, network_state state); -void update_rnn_layer(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_rnn_layer(layer l, network net); +void backward_rnn_layer(layer l, network net); +void update_rnn_layer(layer l, update_args a); #ifdef GPU -void forward_rnn_layer_gpu(layer l, network_state state); -void backward_rnn_layer_gpu(layer l, network_state state); -void update_rnn_layer_gpu(layer l, int batch, float learning_rate, float momentum, float decay); +void forward_rnn_layer_gpu(layer l, network net); +void backward_rnn_layer_gpu(layer l, network net); +void update_rnn_layer_gpu(layer l, update_args a); void push_rnn_layer(layer l); void pull_rnn_layer(layer l); #endif diff --git a/image.darknet/inst/include/darknet/src/route_layer.c b/image.darknet/inst/include/darknet/src/route_layer.c index dce7118..a8970a4 100644 --- a/image.darknet/inst/include/darknet/src/route_layer.c +++ b/image.darknet/inst/include/darknet/src/route_layer.c @@ -1,6 +1,7 @@ #include "route_layer.h" #include "cuda.h" #include "blas.h" + #include route_layer make_route_layer(int batch, int n, int *input_layers, int *input_sizes) @@ -70,13 +71,13 @@ void resize_route_layer(route_layer *l, network *net) } -void forward_route_layer(const route_layer l, network_state state) +void forward_route_layer(const route_layer l, network net) { int i, j; int offset = 0; for(i = 0; i < l.n; ++i){ int index = l.input_layers[i]; - float *input = state.net.layers[index].output; + float *input = net.layers[index].output; int input_size = l.input_sizes[i]; for(j = 0; j < l.batch; ++j){ copy_cpu(input_size, input + j*input_size, 1, l.output + offset + j*l.outputs, 1); @@ -85,13 +86,13 @@ void forward_route_layer(const route_layer l, network_state state) } } -void backward_route_layer(const route_layer l, network_state state) +void backward_route_layer(const route_layer l, network net) { int i, j; int offset = 0; for(i = 0; i < l.n; ++i){ int index = l.input_layers[i]; - float *delta = state.net.layers[index].delta; + float *delta = net.layers[index].delta; int input_size = l.input_sizes[i]; for(j = 0; j < l.batch; ++j){ axpy_cpu(input_size, 1, l.delta + offset + j*l.outputs, 1, delta + j*input_size, 1); @@ -101,31 +102,31 @@ void backward_route_layer(const route_layer l, network_state state) } #ifdef GPU -void forward_route_layer_gpu(const route_layer l, network_state state) +void forward_route_layer_gpu(const route_layer l, network net) { int i, j; int offset = 0; for(i = 0; i < l.n; ++i){ int index = l.input_layers[i]; - float *input = state.net.layers[index].output_gpu; + float *input = net.layers[index].output_gpu; int input_size = l.input_sizes[i]; for(j = 0; j < l.batch; ++j){ - copy_ongpu(input_size, input + j*input_size, 1, l.output_gpu + offset + j*l.outputs, 1); + copy_gpu(input_size, input + j*input_size, 1, l.output_gpu + offset + j*l.outputs, 1); } offset += input_size; } } -void backward_route_layer_gpu(const route_layer l, network_state state) +void backward_route_layer_gpu(const route_layer l, network net) { int i, j; int offset = 0; for(i = 0; i < l.n; ++i){ int index = l.input_layers[i]; - float *delta = state.net.layers[index].delta_gpu; + float *delta = net.layers[index].delta_gpu; int input_size = l.input_sizes[i]; for(j = 0; j < l.batch; ++j){ - axpy_ongpu(input_size, 1, l.delta_gpu + offset + j*l.outputs, 1, delta + j*input_size, 1); + axpy_gpu(input_size, 1, l.delta_gpu + offset + j*l.outputs, 1, delta + j*input_size, 1); } offset += input_size; } diff --git a/image.darknet/inst/include/darknet/src/route_layer.h b/image.darknet/inst/include/darknet/src/route_layer.h index 45467d9..1d40330 100644 --- a/image.darknet/inst/include/darknet/src/route_layer.h +++ b/image.darknet/inst/include/darknet/src/route_layer.h @@ -6,13 +6,13 @@ typedef layer route_layer; route_layer make_route_layer(int batch, int n, int *input_layers, int *input_size); -void forward_route_layer(const route_layer l, network_state state); -void backward_route_layer(const route_layer l, network_state state); +void forward_route_layer(const route_layer l, network net); +void backward_route_layer(const route_layer l, network net); void resize_route_layer(route_layer *l, network *net); #ifdef GPU -void forward_route_layer_gpu(const route_layer l, network_state state); -void backward_route_layer_gpu(const route_layer l, network_state state); +void forward_route_layer_gpu(const route_layer l, network net); +void backward_route_layer_gpu(const route_layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/shortcut_layer.c b/image.darknet/inst/include/darknet/src/shortcut_layer.c index 8bca50f..49d17f5 100644 --- a/image.darknet/inst/include/darknet/src/shortcut_layer.c +++ b/image.darknet/inst/include/darknet/src/shortcut_layer.c @@ -1,12 +1,14 @@ #include "shortcut_layer.h" #include "cuda.h" #include "blas.h" +#include "activations.h" + #include #include layer make_shortcut_layer(int batch, int index, int w, int h, int c, int w2, int h2, int c2) { - fprintf(stderr,"Shortcut Layer: %d\n", index); + fprintf(stderr, "res %3d %4d x%4d x%4d -> %4d x%4d x%4d\n",index, w2,h2,c2, w,h,c); layer l = {0}; l.type = SHORTCUT; l.batch = batch; @@ -36,32 +38,53 @@ layer make_shortcut_layer(int batch, int index, int w, int h, int c, int w2, int return l; } -void forward_shortcut_layer(const layer l, network_state state) +void resize_shortcut_layer(layer *l, int w, int h) +{ + assert(l->w == l->out_w); + assert(l->h == l->out_h); + l->w = l->out_w = w; + l->h = l->out_h = h; + l->outputs = w*h*l->out_c; + l->inputs = l->outputs; + l->delta = realloc(l->delta, l->outputs*l->batch*sizeof(float)); + l->output = realloc(l->output, l->outputs*l->batch*sizeof(float)); + +#ifdef GPU + cuda_free(l->output_gpu); + cuda_free(l->delta_gpu); + l->output_gpu = cuda_make_array(l->output, l->outputs*l->batch); + l->delta_gpu = cuda_make_array(l->delta, l->outputs*l->batch); +#endif + +} + + +void forward_shortcut_layer(const layer l, network net) { - copy_cpu(l.outputs*l.batch, state.input, 1, l.output, 1); - shortcut_cpu(l.batch, l.w, l.h, l.c, state.net.layers[l.index].output, l.out_w, l.out_h, l.out_c, l.output); + copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1); + shortcut_cpu(l.batch, l.w, l.h, l.c, net.layers[l.index].output, l.out_w, l.out_h, l.out_c, l.alpha, l.beta, l.output); activate_array(l.output, l.outputs*l.batch, l.activation); } -void backward_shortcut_layer(const layer l, network_state state) +void backward_shortcut_layer(const layer l, network net) { gradient_array(l.output, l.outputs*l.batch, l.activation, l.delta); - axpy_cpu(l.outputs*l.batch, 1, l.delta, 1, state.delta, 1); - shortcut_cpu(l.batch, l.out_w, l.out_h, l.out_c, l.delta, l.w, l.h, l.c, state.net.layers[l.index].delta); + axpy_cpu(l.outputs*l.batch, l.alpha, l.delta, 1, net.delta, 1); + shortcut_cpu(l.batch, l.out_w, l.out_h, l.out_c, l.delta, l.w, l.h, l.c, 1, l.beta, net.layers[l.index].delta); } #ifdef GPU -void forward_shortcut_layer_gpu(const layer l, network_state state) +void forward_shortcut_layer_gpu(const layer l, network net) { - copy_ongpu(l.outputs*l.batch, state.input, 1, l.output_gpu, 1); - shortcut_gpu(l.batch, l.w, l.h, l.c, state.net.layers[l.index].output_gpu, l.out_w, l.out_h, l.out_c, l.output_gpu); - activate_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation); + copy_gpu(l.outputs*l.batch, net.input_gpu, 1, l.output_gpu, 1); + shortcut_gpu(l.batch, l.w, l.h, l.c, net.layers[l.index].output_gpu, l.out_w, l.out_h, l.out_c, l.alpha, l.beta, l.output_gpu); + activate_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation); } -void backward_shortcut_layer_gpu(const layer l, network_state state) +void backward_shortcut_layer_gpu(const layer l, network net) { - gradient_array_ongpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); - axpy_ongpu(l.outputs*l.batch, 1, l.delta_gpu, 1, state.delta, 1); - shortcut_gpu(l.batch, l.out_w, l.out_h, l.out_c, l.delta_gpu, l.w, l.h, l.c, state.net.layers[l.index].delta_gpu); + gradient_array_gpu(l.output_gpu, l.outputs*l.batch, l.activation, l.delta_gpu); + axpy_gpu(l.outputs*l.batch, l.alpha, l.delta_gpu, 1, net.delta_gpu, 1); + shortcut_gpu(l.batch, l.out_w, l.out_h, l.out_c, l.delta_gpu, l.w, l.h, l.c, 1, l.beta, net.layers[l.index].delta_gpu); } #endif diff --git a/image.darknet/inst/include/darknet/src/shortcut_layer.h b/image.darknet/inst/include/darknet/src/shortcut_layer.h index c09a809..5f684fc 100644 --- a/image.darknet/inst/include/darknet/src/shortcut_layer.h +++ b/image.darknet/inst/include/darknet/src/shortcut_layer.h @@ -5,12 +5,13 @@ #include "network.h" layer make_shortcut_layer(int batch, int index, int w, int h, int c, int w2, int h2, int c2); -void forward_shortcut_layer(const layer l, network_state state); -void backward_shortcut_layer(const layer l, network_state state); +void forward_shortcut_layer(const layer l, network net); +void backward_shortcut_layer(const layer l, network net); +void resize_shortcut_layer(layer *l, int w, int h); #ifdef GPU -void forward_shortcut_layer_gpu(const layer l, network_state state); -void backward_shortcut_layer_gpu(const layer l, network_state state); +void forward_shortcut_layer_gpu(const layer l, network net); +void backward_shortcut_layer_gpu(const layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/softmax_layer.c b/image.darknet/inst/include/darknet/src/softmax_layer.c index 5d15314..9cbc6be 100644 --- a/image.darknet/inst/include/darknet/src/softmax_layer.c +++ b/image.darknet/inst/include/darknet/src/softmax_layer.c @@ -1,6 +1,7 @@ #include "softmax_layer.h" #include "blas.h" #include "cuda.h" + #include #include #include @@ -17,8 +18,10 @@ softmax_layer make_softmax_layer(int batch, int inputs, int groups) l.groups = groups; l.inputs = inputs; l.outputs = inputs; + l.loss = calloc(inputs*batch, sizeof(float)); l.output = calloc(inputs*batch, sizeof(float)); l.delta = calloc(inputs*batch, sizeof(float)); + l.cost = calloc(1, sizeof(float)); l.forward = forward_softmax_layer; l.backward = backward_softmax_layer; @@ -27,45 +30,35 @@ softmax_layer make_softmax_layer(int batch, int inputs, int groups) l.backward_gpu = backward_softmax_layer_gpu; l.output_gpu = cuda_make_array(l.output, inputs*batch); + l.loss_gpu = cuda_make_array(l.loss, inputs*batch); l.delta_gpu = cuda_make_array(l.delta, inputs*batch); #endif return l; } -void softmax_tree(float *input, int batch, int inputs, float temp, tree *hierarchy, float *output) +void forward_softmax_layer(const softmax_layer l, network net) { - int b; - for(b = 0; b < batch; ++b){ + if(l.softmax_tree){ int i; int count = 0; - for(i = 0; i < hierarchy->groups; ++i){ - int group_size = hierarchy->group_size[i]; - softmax(input+b*inputs + count, group_size, temp, output+b*inputs + count); + for (i = 0; i < l.softmax_tree->groups; ++i) { + int group_size = l.softmax_tree->group_size[i]; + softmax_cpu(net.input + count, group_size, l.batch, l.inputs, 1, 0, 1, l.temperature, l.output + count); count += group_size; } + } else { + softmax_cpu(net.input, l.inputs/l.groups, l.batch, l.inputs, l.groups, l.inputs/l.groups, 1, l.temperature, l.output); } -} -void forward_softmax_layer(const softmax_layer l, network_state state) -{ - int b; - int inputs = l.inputs / l.groups; - int batch = l.batch * l.groups; - if(l.softmax_tree){ - softmax_tree(state.input, batch, inputs, l.temperature, l.softmax_tree, l.output); - } else { - for(b = 0; b < batch; ++b){ - softmax(state.input+b*inputs, inputs, l.temperature, l.output+b*inputs); - } + if(net.truth && !l.noloss){ + softmax_x_ent_cpu(l.batch*l.inputs, l.output, net.truth, l.delta, l.loss); + l.cost[0] = sum_array(l.loss, l.batch*l.inputs); } } -void backward_softmax_layer(const softmax_layer l, network_state state) +void backward_softmax_layer(const softmax_layer l, network net) { - int i; - for(i = 0; i < l.inputs*l.batch; ++i){ - state.delta[i] += l.delta[i]; - } + axpy_cpu(l.inputs*l.batch, 1, l.delta, 1, net.delta, 1); } #ifdef GPU @@ -75,26 +68,40 @@ void pull_softmax_layer_output(const softmax_layer layer) cuda_pull_array(layer.output_gpu, layer.output, layer.inputs*layer.batch); } -void forward_softmax_layer_gpu(const softmax_layer l, network_state state) +void forward_softmax_layer_gpu(const softmax_layer l, network net) { - int inputs = l.inputs / l.groups; - int batch = l.batch * l.groups; if(l.softmax_tree){ + softmax_tree(net.input_gpu, 1, l.batch, l.inputs, l.temperature, l.output_gpu, *l.softmax_tree); + /* int i; int count = 0; for (i = 0; i < l.softmax_tree->groups; ++i) { int group_size = l.softmax_tree->group_size[i]; - softmax_gpu(state.input+count, group_size, inputs, batch, l.temperature, l.output_gpu + count); + softmax_gpu(net.input_gpu + count, group_size, l.batch, l.inputs, 1, 0, 1, l.temperature, l.output_gpu + count); count += group_size; } + */ } else { - softmax_gpu(state.input, inputs, inputs, batch, l.temperature, l.output_gpu); + if(l.spatial){ + softmax_gpu(net.input_gpu, l.c, l.batch*l.c, l.inputs/l.c, l.w*l.h, 1, l.w*l.h, 1, l.output_gpu); + }else{ + softmax_gpu(net.input_gpu, l.inputs/l.groups, l.batch, l.inputs, l.groups, l.inputs/l.groups, 1, l.temperature, l.output_gpu); + } + } + if(net.truth && !l.noloss){ + softmax_x_ent_gpu(l.batch*l.inputs, l.output_gpu, net.truth_gpu, l.delta_gpu, l.loss_gpu); + if(l.softmax_tree){ + mask_gpu(l.batch*l.inputs, l.delta_gpu, SECRET_NUM, net.truth_gpu, 0); + mask_gpu(l.batch*l.inputs, l.loss_gpu, SECRET_NUM, net.truth_gpu, 0); + } + cuda_pull_array(l.loss_gpu, l.loss, l.batch*l.inputs); + l.cost[0] = sum_array(l.loss, l.batch*l.inputs); } } -void backward_softmax_layer_gpu(const softmax_layer layer, network_state state) +void backward_softmax_layer_gpu(const softmax_layer layer, network net) { - axpy_ongpu(layer.batch*layer.inputs, 1, layer.delta_gpu, 1, state.delta, 1); + axpy_gpu(layer.batch*layer.inputs, 1, layer.delta_gpu, 1, net.delta_gpu, 1); } #endif diff --git a/image.darknet/inst/include/darknet/src/softmax_layer.h b/image.darknet/inst/include/darknet/src/softmax_layer.h index 821a8dd..2e3ffe0 100644 --- a/image.darknet/inst/include/darknet/src/softmax_layer.h +++ b/image.darknet/inst/include/darknet/src/softmax_layer.h @@ -7,13 +7,13 @@ typedef layer softmax_layer; void softmax_array(float *input, int n, float temp, float *output); softmax_layer make_softmax_layer(int batch, int inputs, int groups); -void forward_softmax_layer(const softmax_layer l, network_state state); -void backward_softmax_layer(const softmax_layer l, network_state state); +void forward_softmax_layer(const softmax_layer l, network net); +void backward_softmax_layer(const softmax_layer l, network net); #ifdef GPU void pull_softmax_layer_output(const softmax_layer l); -void forward_softmax_layer_gpu(const softmax_layer l, network_state state); -void backward_softmax_layer_gpu(const softmax_layer l, network_state state); +void forward_softmax_layer_gpu(const softmax_layer l, network net); +void backward_softmax_layer_gpu(const softmax_layer l, network net); #endif #endif diff --git a/image.darknet/inst/include/darknet/src/stb_image.h b/image.darknet/inst/include/darknet/src/stb_image.h index d0fa9c2..d9c21bc 100644 --- a/image.darknet/inst/include/darknet/src/stb_image.h +++ b/image.darknet/inst/include/darknet/src/stb_image.h @@ -1,5 +1,5 @@ -/* stb_image - v2.06 - public domain image loader - http://nothings.org/stb_image.h - no warranty implied; use at your own risk +/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION @@ -21,17 +21,20 @@ avoid problematic images and only need the trivial interface JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + PNG 1/2/4/8/16-bit-per-channel TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels) + PSD (composited view only, no extra channels, 8/16 bit-per-channel) GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) PNM (PPM and PGM binary only) + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - decode from arbitrary I/O callbacks - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) @@ -39,176 +42,65 @@ Full documentation under "DOCUMENTATION" below. - Revision 2.00 release notes: - - - Progressive JPEG is now supported. - - - PPM and PGM binary formats are now supported, thanks to Ken Miller. - - - x86 platforms now make use of SSE2 SIMD instructions for - JPEG decoding, and ARM platforms can use NEON SIMD if requested. - This work was done by Fabian "ryg" Giesen. SSE2 is used by - default, but NEON must be enabled explicitly; see docs. - - With other JPEG optimizations included in this version, we see - 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup - on a JPEG on an ARM machine, relative to previous versions of this - library. The same results will not obtain for all JPGs and for all - x86/ARM machines. (Note that progressive JPEGs are significantly - slower to decode than regular JPEGs.) This doesn't mean that this - is the fastest JPEG decoder in the land; rather, it brings it - closer to parity with standard libraries. If you want the fastest - decode, look elsewhere. (See "Philosophy" section of docs below.) - - See final bullet items below for more info on SIMD. - - - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing - the memory allocator. Unlike other STBI libraries, these macros don't - support a context parameter, so if you need to pass a context in to - the allocator, you'll have to store it in a global or a thread-local - variable. - - - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and - STBI_NO_LINEAR. - STBI_NO_HDR: suppress implementation of .hdr reader format - STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API - - - You can suppress implementation of any of the decoders to reduce - your code footprint by #defining one or more of the following - symbols before creating the implementation. - - STBI_NO_JPEG - STBI_NO_PNG - STBI_NO_BMP - STBI_NO_PSD - STBI_NO_TGA - STBI_NO_GIF - STBI_NO_HDR - STBI_NO_PIC - STBI_NO_PNM (.ppm and .pgm) - - - You can request *only* certain decoders and suppress all other ones - (this will be more forward-compatible, as addition of new decoders - doesn't require you to disable them explicitly): - - STBI_ONLY_JPEG - STBI_ONLY_PNG - STBI_ONLY_BMP - STBI_ONLY_PSD - STBI_ONLY_TGA - STBI_ONLY_GIF - STBI_ONLY_HDR - STBI_ONLY_PIC - STBI_ONLY_PNM (.ppm and .pgm) - - Note that you can define multiples of these, and you will get all - of them ("only x" and "only y" is interpreted to mean "only x&y"). - - - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still - want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB - - - Compilation of all SIMD code can be suppressed with - #define STBI_NO_SIMD - It should not be necessary to disable SIMD unless you have issues - compiling (e.g. using an x86 compiler which doesn't support SSE - intrinsics or that doesn't support the method used to detect - SSE2 support at run-time), and even those can be reported as - bugs so I can refine the built-in compile-time checking to be - smarter. - - - The old STBI_SIMD system which allowed installing a user-defined - IDCT etc. has been removed. If you need this, don't upgrade. My - assumption is that almost nobody was doing this, and those who - were will find the built-in SIMD more satisfactory anyway. - - - RGB values computed for JPEG images are slightly different from - previous versions of stb_image. (This is due to using less - integer precision in SIMD.) The C code has been adjusted so - that the same RGB values will be computed regardless of whether - SIMD support is available, so your app should always produce - consistent results. But these results are slightly different from - previous versions. (Specifically, about 3% of available YCbCr values - will compute different RGB results from pre-1.49 versions by +-1; - most of the deviating values are one smaller in the G channel.) - - - If you must produce consistent results with previous versions of - stb_image, #define STBI_JPEG_OLD and you will get the same results - you used to; however, you will not get the SIMD speedups for - the YCbCr-to-RGB conversion step (although you should still see - significant JPEG speedup from the other changes). - - Please note that STBI_JPEG_OLD is a temporary feature; it will be - removed in future versions of the library. It is only intended for - near-term back-compatibility use. - - - Latest revision history: - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) additional corruption checking - stbi_set_flip_vertically_on_load - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD - progressive JPEG - PGM/PPM support - STBI_MALLOC,STBI_REALLOC,STBI_FREE - STBI_NO_*, STBI_ONLY_* - GIF bugfix - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) - optimize PNG - fix bug in interlaced PNG with user-specified channel count +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED See end of file for full revision history. ============================ Contributors ========================= - Image formats Bug fixes & warning fixes - Sean Barrett (jpeg, png, bmp) Marc LeBlanc - Nicolas Schulz (hdr, psd) Christpher Lloyd - Jonathan Dummer (tga) Dave Moore - Jean-Marc Lienher (gif) Won Chun - Tom Seddon (pic) the Horde3D community - Thatcher Ulrich (psd) Janez Zemva - Ken Miller (pgm, ppm) Jonathan Blow - Laurent Gomila - Aruelien Pocheville - Extensions, features Ryamond Barbiero - Jetro Lauha (stbi_info) David Woo - Martin "SpartanJ" Golini (stbi_info) Martin Golini - James "moose2000" Brown (iPhone PNG) Roy Eltham - Ben "Disch" Wenger (io callbacks) Luke Graham - Omar Cornut (1/2/4-bit PNG) Thomas Ruf - Nicolas Guillemot (vertical flip) John Bartholomew - Ken Hamada - Optimizations & bugfixes Cort Stratton - Fabian "ryg" Giesen Blazej Dariusz Roszkowski - Arseny Kapoulkine Thibault Reuille - Paul Du Bois - Guillaume George - If your name should be here but Jerry Jansson - isn't, let Sean know. Hayaki Saito - Johan Duparc - Ronny Chevalier - Michal Cichon - Tero Hanninen - Sergio Gonzalez - Cass Everitt - Engin Manap - Martins Mozeiko - Joseph Thomson - Phil Jordan - -License: - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy - and modify this file however you want. - + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine + John-Mark Allen + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan + Dave Moore Roy Eltham Hayaki Saito Nathan Reed + Won Chun Luke Graham Johan Duparc Nick Verigakis + the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar + Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex + Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 + Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus + Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo + Christian Floisand Kevin Schmidt github:darealshinji + Blazej Dariusz Roszkowski github:Michaelangel007 */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -217,10 +109,8 @@ // DOCUMENTATION // // Limitations: -// - no 16-bit-per-channel PNG // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding -// - no 1-bit BMP // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): @@ -233,10 +123,10 @@ // stbi_image_free(data) // // Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *comp -- outputs # of image components in image file -// int req_comp -- if non-zero, # of image components requested in result +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points // to the pixel data, or NULL on an allocation failure or if the image is @@ -244,11 +134,12 @@ // with each pixel consisting of N interleaved 8-bit components; the first // pixel pointed to is top-left-most in the image. There is no padding between // image scanlines or between pixels, regardless of format. The number of -// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. -// If req_comp is non-zero, *comp has the number of components that _would_ -// have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp to see if it's trivially opaque -// because e.g. there were only 3 channels in the source image. +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. // // An output image with N components has the following components interleaved // in this order in each pixel: @@ -260,10 +151,10 @@ // 4 red, green, blue, alpha // // If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() -// can be queried for an extremely brief, end-user unfriendly explanation -// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid -// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly // more user-friendly ones. // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. @@ -282,13 +173,13 @@ // and for best performance I may provide less-easy-to-use APIs that give higher // performance, in addition to the easy to use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which // make more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") -// - Small footprint ("easy to maintain") +// - Small source code footprint ("easy to maintain") // - No dependencies ("ease of use") // // =========================================================================== @@ -320,13 +211,6 @@ // (at least this is true for iOS and Android). Therefore, the NEON support is // toggled by a build flag: define STBI_NEON to get NEON loops. // -// The output of the JPEG decoder is slightly different from versions where -// SIMD support was introduced (that is, for versions before 1.49). The -// difference is only +-1 in the 8-bit RGB channels, and only on a small -// fraction of pixels. You can force the pre-1.49 behavior by defining -// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path -// and hence cost some performance. -// // If for some reason you do not want to use any of SIMD code, or if // you have issues compiling it, you can disable it entirely by // defining STBI_NO_SIMD. @@ -382,6 +266,41 @@ // says there's premultiplied data (currently only happens in iPhone images, // and only if iPhone convert-to-rgb processing is on). // +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// #ifndef STBI_NO_STDIO @@ -392,7 +311,7 @@ enum { - STBI_default = 0, // only used for req_comp + STBI_default = 0, // only used for desired_channels STBI_grey = 1, STBI_grey_alpha = 2, @@ -401,6 +320,7 @@ enum }; typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; #ifdef __cplusplus extern "C" { @@ -428,34 +348,60 @@ typedef struct int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + #ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// #ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif #ifndef STBI_NO_HDR STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif +#endif // STBI_NO_HDR #ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_HDR +#endif // STBI_NO_LINEAR // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); @@ -476,11 +422,14 @@ STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); #endif @@ -561,9 +510,10 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #include // ptrdiff_t on osx #include #include +#include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp +#include // ldexp, pow #endif #ifndef STBI_NO_STDIO @@ -619,18 +569,22 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) #endif -#if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC) +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) // ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) // ok #else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC." +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." #endif #ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,sz) realloc(p,sz) -#define STBI_FREE(p) free(p) +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) #endif // x86/x64 detection @@ -640,12 +594,14 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define STBI__X86_TARGET #endif -#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// NOTE: not clear do we actually need this for the 64-bit path? +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) // gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; -// this is just broken and gcc are jerks for not fixing it properly -// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. #define STBI_NO_SIMD #endif @@ -664,7 +620,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #define STBI_NO_SIMD #endif -#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) #define STBI_SSE2 #include @@ -693,7 +649,7 @@ static int stbi__cpuid3(void) #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name -static int stbi__sse2_available() +static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; @@ -701,16 +657,12 @@ static int stbi__sse2_available() #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -static int stbi__sse2_available() +static int stbi__sse2_available(void) { -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later - // GCC 4.8+ has a nice way to do this - return __builtin_cpu_supports("sse2"); -#else - // portable way to do this, preferably without using GCC inline ASM? - // just bail for now. - return 0; -#endif + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; } #endif #endif @@ -749,7 +701,7 @@ typedef struct stbi_uc buffer_start[128]; stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original; + stbi_uc *img_buffer_original, *img_buffer_original_end; } stbi__context; @@ -761,7 +713,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) s->io.read = NULL; s->read_from_callbacks = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = (stbi_uc *) buffer+len; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } // initialize a callback-based context @@ -773,6 +725,7 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void * s->read_from_callbacks = 1; s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; } #ifndef STBI_NO_STDIO @@ -814,59 +767,76 @@ static void stbi__rewind(stbi__context *s) // but we just rewind to the beginning of the initial buffer, because // we only use it after doing 'test', which only ever looks at at most 92 bytes s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; } +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); -static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG static int stbi__png_test(stbi__context *s); -static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); #endif #ifndef STBI_NO_BMP static int stbi__bmp_test(stbi__context *s); -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA static int stbi__tga_test(stbi__context *s); -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s); -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); #endif #ifndef STBI_NO_HDR static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC static int stbi__pic_test(stbi__context *s); -static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif @@ -889,6 +859,81 @@ static void *stbi__malloc(size_t size) return STBI_MALLOC(size); } +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -901,8 +946,8 @@ static void *stbi__malloc(size_t size) #define stbi__err(x,y) stbi__err(x) #endif -#define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) STBIDEF void stbi_image_free(void *retval_from_stbi_load) { @@ -924,33 +969,38 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) stbi__vertically_flip_on_load = flag_true_if_should_flip; } -static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); #endif #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif @@ -958,58 +1008,138 @@ static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *com #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp); + return stbi__tga_load(s,x,y,comp,req_comp, ri); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } -static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { - unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + int i; + int img_len = w * h * channels; + stbi_uc *reduced; - if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - stbi_uc temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; } } +} - return result; +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; } +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - float temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } - } + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); } } - +#endif #ifndef STBI_NO_STDIO @@ -1041,28 +1171,83 @@ STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req unsigned char *result; stbi__context s; stbi__start_file(&s,f); - result = stbi__load_flip(&s,x,y,comp,req_comp); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + #endif //!STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); - return stbi__load_flip(&s,x,y,comp,req_comp); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_flip(&s,x,y,comp,req_comp); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; } +#endif #ifndef STBI_NO_LINEAR static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) @@ -1070,13 +1255,14 @@ static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); if (hdr_data) stbi__float_postprocess(hdr_data,x,y,comp,req_comp); return hdr_data; } #endif - data = stbi__load_flip(s, x, y, comp, req_comp); + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); @@ -1146,13 +1332,18 @@ STBIDEF int stbi_is_hdr (char const *filename) return result; } -STBIDEF int stbi_is_hdr_from_file(FILE *f) +STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; stbi__context s; stbi__start_file(&s,f); - return stbi__hdr_test(&s); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; #else + STBI_NOTUSED(f); return 0; #endif } @@ -1165,18 +1356,21 @@ STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__hdr_test(&s); #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); return 0; #endif } -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; +#ifndef STBI_NO_LINEAR static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; -#ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } #endif +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } @@ -1285,17 +1479,23 @@ static stbi__uint32 stbi__get32be(stbi__context *s) return (z << 16) + stbi__get16be(s); } +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else static int stbi__get16le(stbi__context *s) { int z = stbi__get8(s); return z + (stbi__get8(s) << 8); } +#endif +#ifndef STBI_NO_BMP static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); return z + (stbi__get16le(s) << 16); } +#endif #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings @@ -1324,7 +1524,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - good = (unsigned char *) stbi__malloc(req_comp * x * y); + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); if (good == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); @@ -1334,26 +1534,75 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r unsigned char *src = data + j * x * img_n ; unsigned char *dest = good + j * x * req_comp; - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; default: STBI_ASSERT(0); } - #undef CASE + #undef STBI__CASE } STBI_FREE(data); @@ -1364,7 +1613,9 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { int i,k,n; - float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -1384,7 +1635,9 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { int i,k,n; - stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -1449,7 +1702,7 @@ typedef struct stbi__context *s; stbi__huffman huff_dc[4]; stbi__huffman huff_ac[4]; - stbi_uc dequant[4][64]; + stbi__uint16 dequant[4][64]; stbi__int16 fast_ac[4][1 << FAST_BITS]; // sizes for components, interleaved MCUs @@ -1485,6 +1738,9 @@ typedef struct int succ_high; int succ_low; int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; int scan_n, order[4]; int restart_interval, todo; @@ -1497,7 +1753,8 @@ typedef struct static int stbi__build_huffman(stbi__huffman *h, int *count) { - int i,j,k=0,code; + int i,j,k=0; + unsigned int code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) for (j=0; j < count[i]; ++j) @@ -1513,7 +1770,7 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) if (h->size[k] == j) { while (h->size[k] == j) h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); } // compute largest code + 1 for this size, preshifted as needed later h->maxcode[j] = code << (16-j); @@ -1554,10 +1811,10 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) // magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); int m = 1 << (magbits - 1); - if (k < m) k += (-1 << magbits) + 1; + if (k < m) k += (~0U << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); } } } @@ -1566,9 +1823,10 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { - int b = j->nomore ? 0 : stbi__get8(j->s); + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes if (c != 0) { j->marker = (unsigned char) c; j->nomore = 1; @@ -1581,7 +1839,7 @@ static void stbi__grow_buffer_unsafe(stbi__jpeg *j) } // (1 << n) - 1 -static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) @@ -1634,7 +1892,7 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) } // bias[n] = (-1<s); if (x != 0xff) return STBI__MARKER_none; while (x == 0xff) - x = stbi__get8(j->s); + x = stbi__get8(j->s); // consume repeated 0xff fill bytes return x; } @@ -2417,7 +2675,7 @@ static void stbi__jpeg_reset(stbi__jpeg *j) j->code_bits = 0; j->code_buffer = 0; j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; j->marker = STBI__MARKER_none; j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; j->eob_run = 0; @@ -2549,7 +2807,7 @@ static int stbi__parse_entropy_coded_data(stbi__jpeg *z) } } -static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) { int i; for (i=0; i < 64; ++i) @@ -2591,13 +2849,14 @@ static int stbi__process_marker(stbi__jpeg *z, int m) L = stbi__get16be(z->s)-2; while (L > 0) { int q = stbi__get8(z->s); - int p = q >> 4; + int p = q >> 4, sixteen = (p != 0); int t = q & 15,i; - if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); - L -= 65; + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); } return L==0; @@ -2630,12 +2889,50 @@ static int stbi__process_marker(stbi__jpeg *z, int m) } return L==0; } + // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - stbi__skip(z->s, stbi__get16be(z->s)-2); + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); return 1; } - return 0; + + return stbi__err("unknown marker","Corrupt JPEG"); } // after we see SOS @@ -2678,6 +2975,28 @@ static int stbi__process_scan_header(stbi__jpeg *z) return 1; } +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; @@ -2687,7 +3006,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires c = stbi__get8(s); - if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; for (i=0; i < c; ++i) { z->img_comp[i].data = NULL; @@ -2696,11 +3015,12 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + z->rgb = 0; for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! - return stbi__err("bad component ID","Corrupt JPEG"); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; q = stbi__get8(s); z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); @@ -2709,7 +3029,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (scan != STBI__SCAN_load) return 1; - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; @@ -2721,6 +3041,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) z->img_v_max = v_max; z->img_mcu_w = h_max * 8; z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; @@ -2732,28 +3053,27 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].data = NULL; - } - return stbi__err("outofmem", "Out of memory"); - } + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); // align blocks for idct using mmx/sse z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; if (z->progressive) { - z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; - z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; - z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } else { - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; } } @@ -2772,6 +3092,8 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 z->marker = STBI__MARKER_none; // initialize cached marker to empty m = stbi__get_marker(z); if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); @@ -2813,12 +3135,15 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) if (x == 255) { j->marker = stbi__get8(j->s); break; - } else if (x != 0) { - return stbi__err("junk before marker", "Corrupt JPEG"); } } // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); } else { if (!stbi__process_marker(j, m)) return 0; } @@ -3037,38 +3362,9 @@ static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_ return out; } -#ifdef STBI_JPEG_OLD -// this is the same YCbCr-to-RGB calculation that stb_image has used -// historically before the algorithm changes in 1.49 -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#else // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar -#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { int i; @@ -3077,9 +3373,9 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; @@ -3093,7 +3389,6 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc out += step; } } -#endif #if defined(STBI_SSE2) || defined(STBI_NEON) static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) @@ -3212,9 +3507,9 @@ static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc cons int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; @@ -3240,18 +3535,14 @@ static void stbi__setup_jpeg(stbi__jpeg *j) #ifdef STBI_SSE2 if (stbi__sse2_available()) { j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif #ifdef STBI_NEON j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } @@ -3259,23 +3550,7 @@ static void stbi__setup_jpeg(stbi__jpeg *j) // clean up the temporary component buffers static void stbi__cleanup_jpeg(stbi__jpeg *j) { - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].raw_data) { - STBI_FREE(j->img_comp[i].raw_data); - j->img_comp[i].raw_data = NULL; - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].raw_coeff) { - STBI_FREE(j->img_comp[i].raw_coeff); - j->img_comp[i].raw_coeff = 0; - j->img_comp[i].coeff = 0; - } - if (j->img_comp[i].linebuf) { - STBI_FREE(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } + stbi__free_jpeg_components(j, j->s->img_n, 0); } typedef struct @@ -3288,9 +3563,16 @@ typedef struct int ypos; // which pre-expansion row we're on } stbi__resample; +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { - int n, decode_n; + int n, decode_n, is_rgb; z->s->img_n = 0; // make stbi__cleanup_jpeg safe // validate req_comp @@ -3300,9 +3582,11 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n; + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - if (z->s->img_n == 3 && n < 3) + if (z->s->img_n == 3 && n < 3 && !is_rgb) decode_n = 1; else decode_n = z->s->img_n; @@ -3339,7 +3623,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp } // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } // now go ahead and resample @@ -3362,7 +3646,39 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (n >= 3) { stbi_uc *y = coutput[0]; if (z->s->img_n == 3) { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } } else for (i=0; i < z->s->img_x; ++i) { out[0] = out[1] = out[2] = y[i]; @@ -3370,37 +3686,70 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp out += n; } } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } } } stbi__cleanup_jpeg(z); *out_x = z->s->img_x; *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n; // report original components, not output + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output return output; } } -static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { - stbi__jpeg j; - j.s = s; - stbi__setup_jpeg(&j); - return load_jpeg_image(&j, x,y,comp,req_comp); + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; } static int stbi__jpeg_test(stbi__context *s) { int r; - stbi__jpeg j; - j.s = s; - stbi__setup_jpeg(&j); - r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); stbi__rewind(s); + STBI_FREE(j); return r; } @@ -3412,15 +3761,18 @@ static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) } if (x) *x = j->s->img_x; if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; return 1; } static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { - stbi__jpeg j; - j.s = s; - return stbi__jpeg_info_raw(&j, x, y, comp); + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; } #endif @@ -3466,7 +3818,7 @@ stbi_inline static int stbi__bit_reverse(int v, int bits) return stbi__bitreverse16(v) >> (16-bits); } -static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) { int i,k=0; int code, next_code[16], sizes[17]; @@ -3501,10 +3853,10 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) z->size [c] = (stbi_uc ) s; z->value[c] = (stbi__uint16) i; if (s <= STBI__ZFAST_BITS) { - int k = stbi__bit_reverse(next_code[s],s); - while (k < (1 << STBI__ZFAST_BITS)) { - z->fast[k] = fastv; - k += (1 << s); + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); } } ++next_code[s]; @@ -3543,7 +3895,7 @@ static void stbi__fill_bits(stbi__zbuf *z) { do { STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= stbi__zget8(z) << z->num_bits; + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); } @@ -3593,14 +3945,15 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; - int cur, limit; + int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); cur = (int) (z->zout - z->zout_start); - limit = (int) (z->zout_end - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); while (cur + n > limit) limit *= 2; - q = (char *) STBI_REALLOC(z->zout_start, limit); + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); z->zout_start = q; z->zout = q + cur; @@ -3608,18 +3961,18 @@ static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room return 1; } -static int stbi__zlength_base[31] = { +static const int stbi__zlength_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; -static int stbi__zlength_extra[31]= +static const int stbi__zlength_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; -static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; -static int stbi__zdist_extra[32] = +static const int stbi__zdist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static int stbi__parse_huffman_block(stbi__zbuf *a) @@ -3666,7 +4019,7 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) static int stbi__compute_huffman_codes(stbi__zbuf *a) { - static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; stbi__zhuffman z_codelength; stbi_uc lencodes[286+32+137];//padding for maximum single op stbi_uc codelength_sizes[19]; @@ -3675,6 +4028,7 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) int hlit = stbi__zreceive(a,5) + 257; int hdist = stbi__zreceive(a,5) + 1; int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { @@ -3684,33 +4038,35 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; - while (n < hlit + hdist) { + while (n < ntot) { int c = stbi__zhuffman_decode(a, &z_codelength); if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; - else if (c == 16) { - c = stbi__zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - memset(lencodes+n, 0, c); + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); n += c; } } - if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; } -static int stbi__parse_uncomperssed_block(stbi__zbuf *a) +static int stbi__parse_uncompressed_block(stbi__zbuf *a) { stbi_uc header[4]; int len,nlen,k; @@ -3752,9 +4108,24 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) return 1; } -// @TODO: should statically initialize these for optimal thread safety -static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; -static void stbi__init_zdefaults(void) +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: { int i; // use <= to match clearly with spec for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; @@ -3764,6 +4135,7 @@ static void stbi__init_zdefaults(void) for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; } +*/ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { @@ -3776,13 +4148,12 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); if (type == 0) { - if (!stbi__parse_uncomperssed_block(a)) return 0; + if (!stbi__parse_uncompressed_block(a)) return 0; } else if (type == 3) { return 0; } else { if (type == 1) { // use fixed code lengths - if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { @@ -3907,7 +4278,7 @@ static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) static int stbi__check_png_header(stbi__context *s) { - static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); @@ -3918,6 +4289,7 @@ typedef struct { stbi__context *s; stbi_uc *idata, *expanded, *out; + int depth; } stbi__png; @@ -3952,35 +4324,40 @@ static int stbi__paeth(int a, int b, int c) return c; } -static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { + int bytes = (depth == 16? 2 : 1); stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n; + stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; int k; int img_n = s->img_n; // copy it into a local for later + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } else { // interlaced: - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; - stbi_uc *prior = cur - stride; + stbi_uc *prior; int filter = *raw++; - int filter_bytes = img_n; - int width = x; + if (filter > 4) return stbi__err("invalid filter","Corrupt PNG"); @@ -3990,6 +4367,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r filter_bytes = 1; width = img_width_bytes; } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; @@ -4013,6 +4391,14 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r raw += img_n; cur += out_n; prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; } else { raw += 1; cur += 1; @@ -4021,38 +4407,47 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r // this is a little gross, so that we don't switch per-pixel or per-component if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*img_n; - #define CASE(f) \ + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ case f: \ for (k=0; k < nk; ++k) switch (filter) { // "none" filter turns into a memcpy here; make that explicit. case STBI__F_none: memcpy(cur, raw, nk); break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; } - #undef CASE + #undef STBI__CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); - #define CASE(f) \ + #define STBI__CASE(f) \ case f: \ - for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ - for (k=0; k < img_n; ++k) + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) switch (filter) { - CASE(STBI__F_none) cur[k] = raw[k]; break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } } - #undef CASE } } @@ -4109,25 +4504,36 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } if (img_n != out_n) { + int q; // insert alpha = 255 - stbi_uc *cur = a->out + stride*j; - int i; + cur = a->out + stride*j; if (img_n == 1) { - for (i=x-1; i >= 0; --i) { - cur[i*2+1] = 255; - cur[i*2+0] = cur[i]; + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; } } else { STBI_ASSERT(img_n == 3); - for (i=x-1; i >= 0; --i) { - cur[i*4+3] = 255; - cur[i*4+2] = cur[i*3+2]; - cur[i*4+1] = cur[i*3+1]; - cur[i*4+0] = cur[i*3+0]; + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; } } } } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } } return 1; @@ -4135,13 +4541,15 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; stbi_uc *final; int p; if (!interlaced) return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing - final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; @@ -4161,8 +4569,8 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 for (i=0; i < x; ++i) { int out_y = j*yspc[p]+yorig[p]; int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, - a->out + (j*x+i)*out_n, out_n); + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); } } STBI_FREE(a->out); @@ -4200,12 +4608,37 @@ static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) return 1; } +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) { stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; stbi_uc *p, *temp_out, *orig = a->out; - p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); if (p == NULL) return stbi__err("outofmem", "Out of memory"); // between here and free(out) below, exitting would leak @@ -4271,9 +4704,10 @@ static void stbi__de_iphone(stbi__png *z) stbi_uc a = p[3]; stbi_uc t = p[0]; if (a) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / a; + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; } else { p[0] = p[2]; p[2] = t; @@ -4292,14 +4726,15 @@ static void stbi__de_iphone(stbi__png *z) } } -#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; stbi_uc has_trans=0, tc[3]; + stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; + int first=1,k,interlace=0, color=0, is_iphone=0; stbi__context *s = z->s; z->expanded = NULL; @@ -4324,8 +4759,9 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); @@ -4373,8 +4809,11 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; - for (k=0; k < s->img_n; ++k) - tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } } break; } @@ -4385,11 +4824,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; stbi_uc *p; if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; while (ioff + c.length > idata_limit) idata_limit *= 2; - p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); z->idata = p; } if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); @@ -4403,7 +4844,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (scan != STBI__SCAN_load) return 1; if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); if (z->expanded == NULL) return 0; // zlib should set error @@ -4412,9 +4853,14 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) s->img_out_n = s->img_n+1; else s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; - if (has_trans) - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) stbi__de_iphone(z); if (pal_img_n) { @@ -4424,6 +4870,9 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (req_comp >= 3) s->img_out_n = req_comp; if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; return 1; @@ -4451,21 +4900,28 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) } } -static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { - unsigned char *result=NULL; + void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { - result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); p->s->img_out_n = req_comp; if (result == NULL) return result; } *x = p->s->img_x; *y = p->s->img_y; - if (n) *n = p->s->img_out_n; + if (n) *n = p->s->img_n; } STBI_FREE(p->out); p->out = NULL; STBI_FREE(p->expanded); p->expanded = NULL; @@ -4474,11 +4930,11 @@ static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req return result; } -static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi__png p; p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp); + return stbi__do_png(&p, x,y,comp,req_comp, ri); } static int stbi__png_test(stbi__context *s) @@ -4507,6 +4963,19 @@ static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) p.s = s; return stbi__png_info_raw(&p, x, y, comp); } + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} #endif // Microsoft/Windows BMP image @@ -4558,36 +5027,46 @@ static int stbi__bitcount(unsigned int a) return a & 0xff; } +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. static int stbi__shiftsigned(int v, int shift, int bits) { - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v >= 0 && v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; } -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +typedef struct { - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; - stbi_uc pal[256][4]; - int psize=0,i,j,compress=0,width; - int bpp, flip_vertically, pad, target, offset, hsz; + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved - offset = stbi__get32le(s); - hsz = stbi__get32le(s); + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { s->img_x = stbi__get16le(s); @@ -4597,15 +5076,9 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int s->img_y = stbi__get32le(s); } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - bpp = stbi__get16le(s); - if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - if (hsz == 12) { - if (bpp < 24) - psize = (offset - 14 - 24) / 3; - } else { - compress = stbi__get32le(s); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres @@ -4619,27 +5092,25 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int stbi__get32le(s); stbi__get32le(s); } - if (bpp == 16 || bpp == 32) { - mr = mg = mb = 0; + if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { - if (bpp == 32) { - mr = 0xffu << 16; - mg = 0xffu << 8; - mb = 0xffu << 0; - ma = 0xffu << 24; - fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 - STBI_NOTUSED(fake_a); + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 } else { - mr = 31u << 10; - mg = 31u << 5; - mb = 31u << 0; + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; } } else if (compress == 3) { - mr = stbi__get32le(s); - mg = stbi__get32le(s); - mb = stbi__get32le(s); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); // not documented, but generated by photoshop and handled by mspaint - if (mr == mg && mg == mb) { + if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? return stbi__errpuc("bad BMP", "bad BMP"); } @@ -4647,11 +5118,13 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int return stbi__errpuc("bad BMP", "bad BMP"); } } else { - STBI_ASSERT(hsz == 108 || hsz == 124); - mr = stbi__get32le(s); - mg = stbi__get32le(s); - mb = stbi__get32le(s); - ma = stbi__get32le(s); + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters @@ -4662,63 +5135,119 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int stbi__get32le(s); // discard reserved } } - if (bpp < 16) - psize = (offset - 14 - hsz) >> 2; } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else target = s->img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (bpp < 16) { + if (info.bpp < 16) { int z=0; if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } for (i=0; i < psize; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); - if (hsz != 12) stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } - stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); - if (bpp == 4) width = (s->img_x + 1) >> 1; - else if (bpp == 8) width = s->img_x; + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (bpp == 4) { - v2 = v & 15; - v >>= 4; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); } - stbi__skip(s, pad); } } else { int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; - stbi__skip(s, offset - 14 - hsz); - if (bpp == 24) width = 3 * s->img_x; - else if (bpp == 16) width = 2*s->img_x; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; pad = (-width) & 3; - if (bpp == 24) { + if (info.bpp == 24) { easy = 1; - } else if (bpp == 32) { + } else if (info.bpp == 32) { if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) easy = 2; } @@ -4739,22 +5268,31 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int out[z+0] = stbi__get8(s); z += 3; a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; if (target == 4) out[z++] = a; } } else { + int bpp = info.bpp; for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - int a; + unsigned int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; if (target == 4) out[z++] = STBI__BYTECAST(a); } } stbi__skip(s, pad); } } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + if (flip_vertically) { stbi_uc t; for (j=0; j < (int) s->img_y>>1; ++j) { @@ -4781,20 +5319,55 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int // Targa Truevision - TGA // by Jonathan Dummer #ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { - int tga_w, tga_h, tga_comp; - int sz; + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; stbi__get8(s); // discard Offset - sz = stbi__get8(s); // color type - if( sz > 1 ) { + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { stbi__rewind(s); return 0; // only RGB or indexed allowed } - sz = stbi__get8(s); // image type - // only RGB or grey allowed, +/- RLE - if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; - stbi__skip(s,9); + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } tga_w = stbi__get16le(s); if( tga_w < 1 ) { stbi__rewind(s); @@ -4805,45 +5378,81 @@ static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) stbi__rewind(s); return 0; // test height } - sz = stbi__get8(s); // bits per pixel - // only RGB or RGBA or grey allowed - if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { - stbi__rewind(s); - return 0; + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; } - tga_comp = sz; if (x) *x = tga_w; if (y) *y = tga_h; - if (comp) *comp = tga_comp / 8; + if (comp) *comp = tga_comp; return 1; // seems to have passed everything } static int stbi__tga_test(stbi__context *s) { - int res; - int sz; + int res = 0; + int sz, tga_color_type; stbi__get8(s); // discard Offset - sz = stbi__get8(s); // color type - if ( sz > 1 ) return 0; // only RGB or indexed allowed + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed sz = stbi__get8(s); // image type - if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE - stbi__get16be(s); // discard palette start - stbi__get16be(s); // discard palette length - stbi__get8(s); // discard bits per palette color entry - stbi__get16be(s); // discard x origin - stbi__get16be(s); // discard y origin - if ( stbi__get16be(s) < 1 ) return 0; // test width - if ( stbi__get16be(s) < 1 ) return 0; // test height + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height sz = stbi__get8(s); // bits per pixel - if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) - res = 0; - else - res = 1; + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: stbi__rewind(s); return res; } -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { // read in the TGA header stuff int tga_offset = stbi__get8(s); @@ -4858,16 +5467,18 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int int tga_width = stbi__get16le(s); int tga_height = stbi__get16le(s); int tga_bits_per_pixel = stbi__get8(s); - int tga_comp = tga_bits_per_pixel / 8; + int tga_comp, tga_rgb16=0; int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) // image data unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; - unsigned char raw_data[4]; + unsigned char raw_data[4] = {0}; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; + STBI_NOTUSED(ri); // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -4875,41 +5486,33 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int tga_image_type -= 8; tga_is_RLE = 1; } - /* int tga_alpha_bits = tga_inverted & 15; */ tga_inverted = 1 - ((tga_inverted >> 5) & 1); - // error check - if ( //(tga_indexed) || - (tga_width < 1) || (tga_height < 1) || - (tga_image_type < 1) || (tga_image_type > 3) || - ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && - (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) - ) - { - return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA - } - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) - { - tga_comp = tga_palette_bits / 8; - } + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); // tga info *x = tga_width; *y = tga_height; if (comp) *comp = tga_comp; - tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) stbi__skip(s, tga_offset ); - if ( !tga_indexed && !tga_is_RLE) { + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { for (i=0; i < tga_height; ++i) { - int y = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + y*tga_width*tga_comp; + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; stbi__getn(s, tga_row, tga_width * tga_comp); } } else { @@ -4919,15 +5522,22 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette - tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 ); + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); } - if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); } } // load the data @@ -4957,23 +5567,22 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int // load however much data we did have if ( tga_indexed ) { - // read in 1 byte, then perform the lookup - int pal_idx = stbi__get8(s); - if ( pal_idx >= tga_palette_len ) - { - // invalid index + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index pal_idx = 0; } - pal_idx *= tga_bits_per_pixel / 8; - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { raw_data[j] = tga_palette[pal_idx+j]; } - } else - { + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { // read in the data raw - for (j = 0; j*8 < tga_bits_per_pixel; ++j) - { + for (j = 0; j < tga_comp; ++j) { raw_data[j] = stbi__get8(s); } } @@ -5012,8 +5621,8 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int } } - // swap RGB - if (tga_comp >= 3) + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) { unsigned char* tga_pixel = tga_data; for (i=0; i < tga_width * tga_height; ++i) @@ -5049,13 +5658,53 @@ static int stbi__psd_test(stbi__context *s) return r; } -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { - int pixelCount; + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; int channelCount, compression; - int channel, i, count, len; + int channel, i; + int bitdepth; int w,h; stbi_uc *out; + STBI_NOTUSED(ri); // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" @@ -5078,8 +5727,9 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int w = stbi__get32be(s); // Make sure the depth is 8 bits. - if (stbi__get16be(s) != 8) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); // Make sure the color mode is RGB. // Valid options are: @@ -5111,8 +5761,18 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int if (compression > 1) return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + // Create the destination image. - out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; @@ -5144,61 +5804,86 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); } } } } else { // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out + channel; - if (channel > channelCount) { + if (channel >= channelCount) { // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = channel == 3 ? 255 : 0; + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } } else { - // Read the data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } } } } + // convert to desired output format if (req_comp && req_comp != 4) { - out = stbi__convert_format(out, 4, req_comp, w, h); + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); if (out == NULL) return out; // stbi__convert_format frees input on failure } @@ -5350,7 +6035,6 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c if (count >= 128) { // Repeated stbi_uc value[4]; - int i; if (count==128) count = stbi__get16be(s); @@ -5383,10 +6067,13 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c return result; } -static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) { stbi_uc *result; - int i, x,y; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; for (i=0; i<92; ++i) stbi__get8(s); @@ -5394,14 +6081,14 @@ static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int re x = stbi__get16be(s); y = stbi__get16be(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); stbi__get32be(s); //skip `ratio' stbi__get16be(s); //skip `fields' stbi__get16be(s); //skip `pad' // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc(x*y*4); + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { @@ -5439,10 +6126,12 @@ typedef struct { int w,h; stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; - stbi__gif_lzw codes[4096]; + stbi__gif_lzw codes[8192]; stbi_uc *color_table; int parse, step; int lflags; @@ -5450,6 +6139,7 @@ typedef struct int max_x, max_y; int cur_x, cur_y; int line_size; + int delay; } stbi__gif; static int stbi__gif_test_raw(stbi__context *s) @@ -5510,19 +6200,22 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { - stbi__gif g; - if (!stbi__gif_header(s, &g, comp, 1)) { + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); stbi__rewind( s ); return 0; } - if (x) *x = g.w; - if (y) *y = g.h; + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); return 1; } static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -5531,10 +6224,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; - if (c[3] >= 128) { + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -5557,7 +6252,7 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) { stbi_uc lzw_cs; - stbi__int32 len, code; + stbi__int32 len, init_code; stbi__uint32 first; stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; stbi__gif_lzw *p; @@ -5570,10 +6265,10 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) codemask = (1 << codesize) - 1; bits = 0; valid_bits = 0; - for (code = 0; code < clear; code++) { - g->codes[code].prefix = -1; - g->codes[code].first = (stbi_uc) code; - g->codes[code].suffix = (stbi_uc) code; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; } // support no starting clear code @@ -5608,11 +6303,16 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) stbi__skip(s,len); return g->out; } else if (code <= avail) { - if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } if (oldcode >= 0) { p = &g->codes[avail++]; - if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + p->prefix = (stbi__int16) oldcode; p->first = g->codes[oldcode].first; p->suffix = (code == avail) ? p->first : g->codes[code].first; @@ -5634,43 +6334,70 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) } } -static void stbi__fill_gif_background(stbi__gif *g) -{ - int i; - stbi_uc *c = g->pal[g->bgindex]; - // @OPTIMIZE: write a dword at a time - for (i = 0; i < g->w * g->h * 4; i += 4) { - stbi_uc *p = &g->out[i]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } -} - // this function is designed to support animated gifs, although stb_image doesn't support it -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { - int i; - stbi_uc *old_out = 0; + int dispose; + int first_frame; + int pi; + int pcount; + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; if (g->out == 0) { if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->history = (stbi_uc *) stbi__malloc(g->w * g->h); if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - stbi__fill_gif_background(g); + + // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to teh color that was there the previous frame. + memset( g->out, 0x00, 4 * g->w * g->h ); + memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + first_frame = 1; } else { - // animated-gif-only path - if (((g->eflags & 0x1C) >> 2) == 3) { - old_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - memcpy(g->out, old_out, g->w*g->h*4); + // second frame - how do we dispoase of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + for (;;) { - switch (stbi__get8(s)) { + int tag = stbi__get8(s); + switch (tag) { case 0x2C: /* Image Descriptor */ { stbi__int32 x, y, w, h; @@ -5705,38 +6432,60 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { - for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent - g->pal[i][3] = 255; - if (g->transparent >= 0 && (g->eflags & 0x01)) - g->pal[g->transparent][3] = 0; g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - + return stbi__errpuc("missing color table", "Corrupt GIF"); + o = stbi__process_gif_raster(s, g); if (o == NULL) return NULL; - if (req_comp && req_comp != 4) - o = stbi__convert_format(o, 4, req_comp, g->w, g->h); + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + return o; } case 0x21: // Comment Extension. { int len; - if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); - stbi__get16le(s); // delay - g->transparent = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } } else { stbi__skip(s, len); break; } - } - while ((len = stbi__get8(s)) != 0) + } + while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); + } break; } @@ -5749,19 +6498,90 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } } -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (delays) { + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); - u = stbi__gif_load_next(s, &g, comp, req_comp); + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + return u; } @@ -5775,20 +6595,24 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s) +static int stbi__hdr_test_core(stbi__context *s, const char *signature) { - const char *signature = "#?RADIANCE\n"; int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) - return 0; + return 0; + stbi__rewind(s); return 1; } static int stbi__hdr_test(stbi__context* s) { - int r = stbi__hdr_test_core(s); + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } return r; } @@ -5842,7 +6666,7 @@ static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) } } -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { char buffer[STBI__HDR_BUFLEN]; char *token; @@ -5853,10 +6677,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re int len; unsigned char count, value; int i, j, k, c1,c2, z; - + const char *headerToken; + STBI_NOTUSED(ri); // Check identifier - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) return stbi__errpf("not HDR", "Corrupt HDR image"); // Parse header @@ -5885,8 +6711,13 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re if (comp) *comp = 3; if (req_comp == 0) req_comp = 3; + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + // Read data - hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); // Load image data // image data is stored as some number of sca @@ -5925,20 +6756,29 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re len <<= 8; len |= stbi__get8(s); if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } for (k = 0; k < 4; ++k) { + int nleft; i = 0; - while (i < width) { + while ((nleft = width - i) > 0) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -5947,7 +6787,8 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re for (i=0; i < width; ++i) stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); } - STBI_FREE(scanline); + if (scanline) + STBI_FREE(scanline); } return hdr_data; @@ -5958,8 +6799,13 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + if (stbi__hdr_test(s) == 0) { stbi__rewind( s ); return 0; } @@ -5996,29 +6842,17 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_BMP static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) { - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') { - stbi__rewind( s ); - return 0; - } - stbi__skip(s,12); - hsz = stbi__get32le(s); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) { - stbi__rewind( s ); - return 0; - } - if (hsz == 12) { - *x = stbi__get16le(s); - *y = stbi__get16le(s); - } else { - *x = stbi__get32le(s); - *y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) { - stbi__rewind( s ); - return 0; - } - *comp = stbi__get16le(s) / 8; + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) *comp = info.ma ? 4 : 3; return 1; } #endif @@ -6026,7 +6860,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { - int channelCount; + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; @@ -6043,7 +6880,8 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) } *y = stbi__get32be(s); *x = stbi__get32be(s); - if (stbi__get16be(s) != 8) { + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { stbi__rewind( s ); return 0; } @@ -6054,22 +6892,61 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) *comp = 4; return 1; } + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} #endif #ifndef STBI_NO_PIC static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { - int act_comp=0,num_packets=0,chained; + int act_comp=0,num_packets=0,chained,dummy; stbi__pic_packet packets[10]; - stbi__skip(s, 92); + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); *x = stbi__get16be(s); *y = stbi__get16be(s); - if (stbi__at_eof(s)) return 0; + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } stbi__skip(s, 8); @@ -6129,16 +7006,22 @@ static int stbi__pnm_test(stbi__context *s) return 1; } -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; + STBI_NOTUSED(ri); + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; + *x = s->img_x; *y = s->img_y; - *comp = s->img_n; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); - out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); stbi__getn(s, out, s->img_n * s->img_x * s->img_y); @@ -6156,8 +7039,16 @@ static int stbi__pnm_isspace(char c) static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } } static int stbi__pnm_isdigit(char c) @@ -6179,16 +7070,20 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { - int maxv; + int maxv, dummy; char c, p, t; - stbi__rewind( s ); + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); // Get identifier p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); + stbi__rewind(s); return 0; } @@ -6254,6 +7149,19 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + #ifndef STBI_NO_STDIO STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { @@ -6275,6 +7183,27 @@ STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) fseek(f,pos,SEEK_SET); return r; } + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} #endif // !STBI_NO_STDIO STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) @@ -6291,10 +7220,63 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int return stbi__info_main(&s,x,y,comp); } +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + #endif // STB_IMAGE_IMPLEMENTATION /* revision history: + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit @@ -6435,3 +7417,46 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int 0.50 (2006-11-19) first released version */ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/image.darknet/inst/include/darknet/src/stb_image_write.h b/image.darknet/inst/include/darknet/src/stb_image_write.h index f5250b3..c05e958 100644 --- a/image.darknet/inst/include/darknet/src/stb_image_write.h +++ b/image.darknet/inst/include/darknet/src/stb_image_write.h @@ -1,7 +1,6 @@ -/* stb_image_write - v0.98 - public domain - http://nothings.org/stb/stb_image_write.h - writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 - no warranty implied; use at your own risk - +/* stb_image_write - v1.09 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk Before #including, @@ -11,31 +10,67 @@ Will probably not work correctly with strict-aliasing optimizations. + If using a modern Microsoft Compiler, non-safe versions of CRT calls may cause + compilation warnings or even errors. To avoid this, also before #including, + + #define STBI_MSC_SECURE_CRT + ABOUT: This header file is a library for writing images to C stdio. It could be adapted to write to memory or a general streaming interface; let me know. The PNG output is not optimal; it is 20-50% larger than the file - written by a decent optimizing implementation. This library is designed - for source code compactness and simplicitly, not optimal image file size - or run-time performance. + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. - You can define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), USAGE: - There are four functions, one for each image file format: + There are five functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); - int stbi_write_hdr(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. Each function returns 0 on failure and non-0 on success. @@ -59,63 +94,138 @@ writer, both because it is in BGR order and because it may have padding at the end of the line.) + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + CREDITS: - PNG/BMP/TGA - Sean Barrett - HDR - Baldur Karlsson - TGA monochrome: - Jean-Sebastien Guay - misc enhancements: - Tim Kelsey + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + bugfixes: github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + +LICENSE + + See end of file for license information. + */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else #ifdef __cplusplus -extern "C" { +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif #endif -extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); -extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); -extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); -extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +extern int stbi_write_tga_with_rle; +extern int stbi_write_png_compression_level; +extern int stbi_write_force_png_filter; +#endif -#ifdef __cplusplus -} +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #endif +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + #endif//INCLUDE_STB_IMAGE_WRITE_H #ifdef STB_IMAGE_WRITE_IMPLEMENTATION +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + #include #include -#include #include #include -#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC) +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) // ok -#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) // ok #else -#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC." +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." #endif #ifndef STBIW_MALLOC -#define STBIW_MALLOC(sz) malloc(sz) -#define STBIW_REALLOC(p,sz) realloc(p,sz) -#define STBIW_FREE(p) free(p) +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) #endif + + #ifndef STBIW_MEMMOVE #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) #endif @@ -126,22 +236,90 @@ extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const fl #define STBIW_ASSERT(x) assert(x) #endif +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi__flip_vertically_on_write=0; +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi__flip_vertically_on_write=0; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f; +#ifdef STBI_MSC_SECURE_CRT + if (fopen_s(&f, filename, "wb")) + f = NULL; +#else + f = fopen(filename, "wb"); +#endif + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; -static void writefv(FILE *f, const char *fmt, va_list v) +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { switch (*fmt++) { case ' ': break; - case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; } - case '2': { int x = va_arg(v,int); unsigned char b[2]; - b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8); - fwrite(b,2,1,f); break; } - case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; - b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8); - b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24); - fwrite(b,4,1,f); break; } + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } default: STBIW_ASSERT(0); return; @@ -149,22 +327,70 @@ static void writefv(FILE *f, const char *fmt, va_list v) } } -static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c) +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { unsigned char arr[3]; arr[0] = a, arr[1] = b, arr[2] = c; - fwrite(arr, 3, 1, f); + s->func(s->context, arr, 3); } -static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) { unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ stbiw_uint32 zero = 0; - int i,j,k, j_end; + int i,j, j_end; if (y <= 0) return; + if (stbi__flip_vertically_on_write) + vdir *= -1; + if (vdir < 0) j_end = -1, j = y-1; else @@ -173,73 +399,157 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, for (; j != j_end; j += vdir) { for (i=0; i < x; ++i) { unsigned char *d = (unsigned char *) data + (j*x+i)*comp; - if (write_alpha < 0) - fwrite(&d[comp-1], 1, 1, f); - switch (comp) { - case 1: fwrite(d, 1, 1, f); - break; - case 2: if (expand_mono) - write3(f, d[0],d[0],d[0]); // monochrome bmp - else - fwrite(d, 1, 1, f); // monochrome TGA - break; - case 4: - if (!write_alpha) { - // composite against pink background - for (k=0; k < 3; ++k) - px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255; - write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]); - break; - } - /* FALLTHROUGH */ - case 3: - write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]); - break; - } - if (write_alpha > 0) - fwrite(&d[comp-1], 1, 1, f); + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } - fwrite(&zero,scanline_pad,1,f); + s->func(s->context, &zero, scanline_pad); } } -static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) { - FILE *f; - if (y < 0 || x < 0) return 0; - f = fopen(filename, "wb"); - if (f) { + if (y < 0 || x < 0) { + return 0; + } else { va_list v; va_start(v, fmt); - writefv(f, fmt, v); + stbiw__writefv(s, fmt, v); va_end(v); - write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad,expand_mono); - fclose(f); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; } - return f != NULL; } -int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { int pad = (-x*3) & 3; - return outfile(filename,-1,-1,x,y,comp,1,(void *) data,0,pad, + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, "11 4 22 4" "4 44 22 444444", 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header } -int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) { int has_alpha = (comp == 2 || comp == 4); int colorbytes = has_alpha ? comp-1 : comp; int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 - return outfile(filename, -1,-1, x, y, comp, 0, (void *) data, has_alpha, 0, - "111 221 2222 11", 0,0,format, 0,0,0, 0,0,x,y, (colorbytes+has_alpha)*8, has_alpha*8); + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; } +#endif // ************************************************************************************************* // Radiance RGBE HDR writer // by Baldur Karlsson + #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) @@ -247,7 +557,7 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) int exponent; float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); - if (maxcomp < 1e-32) { + if (maxcomp < 1e-32f) { rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; } else { float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; @@ -259,27 +569,27 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) } } -void stbiw__write_run_data(FILE *f, int length, unsigned char databyte) +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { - unsigned char lengthbyte = (unsigned char) (length+128); + unsigned char lengthbyte = STBIW_UCHAR(length+128); STBIW_ASSERT(length+128 <= 255); - fwrite(&lengthbyte, 1, 1, f); - fwrite(&databyte, 1, 1, f); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); } -void stbiw__write_dump_data(FILE *f, int length, unsigned char *data) +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { - unsigned char lengthbyte = (unsigned char )(length & 0xff); + unsigned char lengthbyte = STBIW_UCHAR(length); STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code - fwrite(&lengthbyte, 1, 1, f); - fwrite(data, length, 1, f); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); } -void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scratch, const float *scanline) +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char rgbe[4]; - float linear[3] = {0}; + float linear[3]; int x; scanlineheader[2] = (width&0xff00)>>8; @@ -288,31 +598,31 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra /* skip RLE for images too small or large */ if (width < 8 || width >= 32768) { for (x=0; x < width; x++) { - switch (comp) { + switch (ncomp) { case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*comp + 2]; - linear[1] = scanline[x*comp + 1]; - linear[0] = scanline[x*comp + 0]; + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; break; - case 2: /* fallthrough */ - case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0]; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); - fwrite(rgbe, 4, 1, f); + s->func(s->context, rgbe, 4); } } else { int c,r; /* encode into scratch buffer */ for (x=0; x < width; x++) { - switch(comp) { + switch(ncomp) { case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*comp + 2]; - linear[1] = scanline[x*comp + 1]; - linear[0] = scanline[x*comp + 0]; + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; break; - case 2: /* fallthrough */ - case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0]; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); @@ -322,7 +632,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra scratch[x + width*3] = rgbe[3]; } - fwrite(scanlineheader, 4, 1, f); + s->func(s->context, scanlineheader, 4); /* RLE each component separately */ for (c=0; c < 4; c++) { @@ -343,7 +653,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra while (x < r) { int len = r-x; if (len > 128) len = 128; - stbiw__write_dump_data(f, len, &comp[x]); + stbiw__write_dump_data(s, len, &comp[x]); x += len; } // if there's a run, output it @@ -355,7 +665,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra while (x < r) { int len = r-x; if (len > 127) len = 127; - stbiw__write_run_data(f, len, comp[x]); + stbiw__write_run_data(s, len, comp[x]); x += len; } } @@ -364,28 +674,59 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra } } -int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) { - int i; - FILE *f; - if (y <= 0 || x <= 0 || data == NULL) return 0; - f = fopen(filename, "wb"); - if (f) { - /* Each component is stored separately. Allocate scratch space for full output scanline. */ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); - fprintf(f, "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n" ); - fprintf(f, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n" , y, x); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef STBI_MSC_SECURE_CRT + len = sprintf_s(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + for(i=0; i < y; i++) - stbiw__write_hdr_scanline(f, x, comp, scratch, data + comp*i*x); + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)*x); STBIW_FREE(scratch); - fclose(f); + return 1; } - return f != NULL; } -///////////////////////////////////////////////////////// -// PNG +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #define stbiw__sbraw(a) ((int *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] @@ -402,7 +743,7 @@ int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *da static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; - void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); STBIW_ASSERT(p); if (p) { if (!*arr) ((int *) p)[1] = 0; @@ -415,7 +756,7 @@ static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { while (*bitcount >= 8) { - stbiw__sbpush(data, (unsigned char) *bitbuffer); + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); *bitbuffer >>= 8; *bitcount -= 8; } @@ -466,8 +807,14 @@ static unsigned int stbiw__zhash(unsigned char *data) #define stbiw__ZHASH 16384 +#endif // STBIW_ZLIB_COMPRESS + unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; @@ -475,7 +822,9 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; - unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack! + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (hash_table == NULL) + return NULL; if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window @@ -547,43 +896,77 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l for (i=0; i < stbiw__ZHASH; ++i) (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); { // compute adler32 on input - unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552; - int j=0; + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; while (j < data_len) { for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; s1 %= 65521, s2 %= 65521; j += blocklen; blocklen = 5552; } - stbiw__sbpush(out, (unsigned char) (s2 >> 8)); - stbiw__sbpush(out, (unsigned char) s2); - stbiw__sbpush(out, (unsigned char) (s1 >> 8)); - stbiw__sbpush(out, (unsigned char) s1); + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); } *out_len = stbiw__sbn(out); // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS } -unsigned int stbiw__crc32(unsigned char *buffer, int len) +static unsigned int stbiw__crc32(unsigned char *buffer, int len) { - static unsigned int crc_table[256]; + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + unsigned int crc = ~0u; - int i,j; - if (crc_table[1] == 0) - for(i=0; i < 256; i++) - for (crc_table[i]=i, j=0; j < 8; ++j) - crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0); + int i; for (i=0; i < len; ++i) crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; return ~crc; } -#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4) +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) @@ -596,66 +979,94 @@ static void stbiw__wpcrc(unsigned char **data, int len) static unsigned char stbiw__paeth(int a, int b, int c) { int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); - if (pa <= pb && pa <= pc) return (unsigned char) a; - if (pb <= pc) return (unsigned char) b; - return (unsigned char) c; + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + for (i = 0; i < n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + for (i=n; i < width*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } } unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { + int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; - int i,j,k,p,zlen; + int j,zlen; if (stride_bytes == 0) stride_bytes = x * n; + if (force_filter >= 5) { + force_filter = -1; + } + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { - static int mapping[] = { 0,1,2,3,4 }; - static int firstmap[] = { 0,1,0,5,6 }; - int *mymap = j ? mapping : firstmap; - int best = 0, bestval = 0x7fffffff; - for (p=0; p < 2; ++p) { - for (k= p?best:0; k < 5; ++k) { - int type = mymap[k],est=0; - unsigned char *z = pixels + stride_bytes*j; - for (i=0; i < n; ++i) - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; - case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; - case 5: line_buffer[i] = z[i]; break; - case 6: line_buffer[i] = z[i]; break; - } - for (i=n; i < x*n; ++i) { - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i] - z[i-n]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; - case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; - case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; - } - } - if (p) break; - for (i=0; i < x*n; ++i) + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); - if (est < bestval) { bestval = est; best = k; } + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; } } - // when we get here, best contains the filter type, and line_buffer contains the data - filt[j*(x*n+1)] = (unsigned char) best; + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); - zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); STBIW_FREE(filt); if (!zlib) return 0; @@ -671,7 +1082,7 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in stbiw__wp32(o, x); stbiw__wp32(o, y); *o++ = 8; - *o++ = (unsigned char) ctype[n]; + *o++ = STBIW_UCHAR(ctype[n]); *o++ = 0; *o++ = 0; *o++ = 0; @@ -693,22 +1104,407 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in return out; } -int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { FILE *f; int len; unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); - if (!png) return 0; + if (png == NULL) return 0; +#ifdef STBI_MSC_SECURE_CRT + if (fopen_s(&f, filename, "wb")) + f = NULL; +#else f = fopen(filename, "wb"); +#endif if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); STBIW_FREE(png); return 1; } +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + const unsigned char *imageData = (const unsigned char *)data; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + int x, y, pos; + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float YDU[64], UDU[64], VDU[64]; + for(row = y, pos = 0; row < y+8; ++row) { + for(col = x; col < x+8; ++col, ++pos) { + int p = (stbi__flip_vertically_on_write ? height-1-row : row)*width*comp + col*comp; + float r, g, b; + if(row >= height) { + p -= width*comp*(row+1 - height); + } + if(col >= width) { + p -= comp*(col+1 - width); + } + + r = imageData[p+0]; + g = imageData[p+ofsG]; + b = imageData[p+ofsB]; + YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; + UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; + VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support 0.98 (2015-04-08) added STBIW_MALLOC, STBIW_ASSERT etc 0.97 (2015-01-18) @@ -728,3 +1524,45 @@ int stbi_write_png(char const *filename, int x, int y, int comp, const void *dat first public release 0.90 first internal release */ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/image.darknet/inst/include/darknet/src/tree.c b/image.darknet/inst/include/darknet/src/tree.c index dd44515..67b6d43 100644 --- a/image.darknet/inst/include/darknet/src/tree.c +++ b/image.darknet/inst/include/darknet/src/tree.c @@ -24,33 +24,33 @@ void change_leaves(tree *t, char *leaf_list) fprintf(stderr, "Found %d leaves.\n", found); } -float get_hierarchy_probability(float *x, tree *hier, int c) +float get_hierarchy_probability(float *x, tree *hier, int c, int stride) { float p = 1; while(c >= 0){ - p = p * x[c]; + p = p * x[c*stride]; c = hier->parent[c]; } return p; } -void hierarchy_predictions(float *predictions, int n, tree *hier, int only_leaves) +void hierarchy_predictions(float *predictions, int n, tree *hier, int only_leaves, int stride) { int j; for(j = 0; j < n; ++j){ int parent = hier->parent[j]; if(parent >= 0){ - predictions[j] *= predictions[parent]; + predictions[j*stride] *= predictions[parent*stride]; } } if(only_leaves){ for(j = 0; j < n; ++j){ - if(!hier->leaf[j]) predictions[j] = 0; + if(!hier->leaf[j]) predictions[j*stride] = 0; } } } -int hierarchy_top_prediction(float *predictions, tree *hier, float thresh) +int hierarchy_top_prediction(float *predictions, tree *hier, float thresh, int stride) { float p = 1; int group = 0; @@ -61,7 +61,7 @@ int hierarchy_top_prediction(float *predictions, tree *hier, float thresh) for(i = 0; i < hier->group_size[group]; ++i){ int index = i + hier->group_offset[group]; - float val = predictions[i + hier->group_offset[group]]; + float val = predictions[(i + hier->group_offset[group])*stride]; if(val > max){ max_i = index; max = val; @@ -71,6 +71,8 @@ int hierarchy_top_prediction(float *predictions, tree *hier, float thresh) p = p*max; group = hier->child[max_i]; if(hier->child[max_i] < 0) return max_i; + } else if (group == 0){ + return max_i; } else { return hier->parent[hier->group_offset[group]]; } diff --git a/image.darknet/inst/include/darknet/src/tree.h b/image.darknet/inst/include/darknet/src/tree.h index dbd4c39..3802b8e 100644 --- a/image.darknet/inst/include/darknet/src/tree.h +++ b/image.darknet/inst/include/darknet/src/tree.h @@ -1,23 +1,8 @@ #ifndef TREE_H #define TREE_H +#include "darknet.h" -typedef struct{ - int *leaf; - int n; - int *parent; - int *child; - int *group; - char **name; - - int groups; - int *group_size; - int *group_offset; -} tree; - -tree *read_tree(char *filename); -void hierarchy_predictions(float *predictions, int n, tree *hier, int only_leaves); -void change_leaves(tree *t, char *leaf_list); -int hierarchy_top_prediction(float *predictions, tree *hier, float thresh); -float get_hierarchy_probability(float *x, tree *hier, int c); +int hierarchy_top_prediction(float *predictions, tree *hier, float thresh, int stride); +float get_hierarchy_probability(float *x, tree *hier, int c, int stride); #endif diff --git a/image.darknet/inst/include/darknet/src/upsample_layer.c b/image.darknet/inst/include/darknet/src/upsample_layer.c new file mode 100644 index 0000000..605f21f --- /dev/null +++ b/image.darknet/inst/include/darknet/src/upsample_layer.c @@ -0,0 +1,106 @@ +#include "upsample_layer.h" +#include "cuda.h" +#include "blas.h" + +#include + +layer make_upsample_layer(int batch, int w, int h, int c, int stride) +{ + layer l = {0}; + l.type = UPSAMPLE; + l.batch = batch; + l.w = w; + l.h = h; + l.c = c; + l.out_w = w*stride; + l.out_h = h*stride; + l.out_c = c; + if(stride < 0){ + stride = -stride; + l.reverse=1; + l.out_w = w/stride; + l.out_h = h/stride; + } + l.stride = stride; + l.outputs = l.out_w*l.out_h*l.out_c; + l.inputs = l.w*l.h*l.c; + l.delta = calloc(l.outputs*batch, sizeof(float)); + l.output = calloc(l.outputs*batch, sizeof(float));; + + l.forward = forward_upsample_layer; + l.backward = backward_upsample_layer; + #ifdef GPU + l.forward_gpu = forward_upsample_layer_gpu; + l.backward_gpu = backward_upsample_layer_gpu; + + l.delta_gpu = cuda_make_array(l.delta, l.outputs*batch); + l.output_gpu = cuda_make_array(l.output, l.outputs*batch); + #endif + if(l.reverse) fprintf(stderr, "downsample %2dx %4d x%4d x%4d -> %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c); + else fprintf(stderr, "upsample %2dx %4d x%4d x%4d -> %4d x%4d x%4d\n", stride, w, h, c, l.out_w, l.out_h, l.out_c); + return l; +} + +void resize_upsample_layer(layer *l, int w, int h) +{ + l->w = w; + l->h = h; + l->out_w = w*l->stride; + l->out_h = h*l->stride; + if(l->reverse){ + l->out_w = w/l->stride; + l->out_h = h/l->stride; + } + l->outputs = l->out_w*l->out_h*l->out_c; + l->inputs = l->h*l->w*l->c; + l->delta = realloc(l->delta, l->outputs*l->batch*sizeof(float)); + l->output = realloc(l->output, l->outputs*l->batch*sizeof(float)); + +#ifdef GPU + cuda_free(l->output_gpu); + cuda_free(l->delta_gpu); + l->output_gpu = cuda_make_array(l->output, l->outputs*l->batch); + l->delta_gpu = cuda_make_array(l->delta, l->outputs*l->batch); +#endif + +} + +void forward_upsample_layer(const layer l, network net) +{ + fill_cpu(l.outputs*l.batch, 0, l.output, 1); + if(l.reverse){ + upsample_cpu(l.output, l.out_w, l.out_h, l.c, l.batch, l.stride, 0, l.scale, net.input); + }else{ + upsample_cpu(net.input, l.w, l.h, l.c, l.batch, l.stride, 1, l.scale, l.output); + } +} + +void backward_upsample_layer(const layer l, network net) +{ + if(l.reverse){ + upsample_cpu(l.delta, l.out_w, l.out_h, l.c, l.batch, l.stride, 1, l.scale, net.delta); + }else{ + upsample_cpu(net.delta, l.w, l.h, l.c, l.batch, l.stride, 0, l.scale, l.delta); + } +} + +#ifdef GPU +void forward_upsample_layer_gpu(const layer l, network net) +{ + fill_gpu(l.outputs*l.batch, 0, l.output_gpu, 1); + if(l.reverse){ + upsample_gpu(l.output_gpu, l.out_w, l.out_h, l.c, l.batch, l.stride, 0, l.scale, net.input_gpu); + }else{ + upsample_gpu(net.input_gpu, l.w, l.h, l.c, l.batch, l.stride, 1, l.scale, l.output_gpu); + } +} + +void backward_upsample_layer_gpu(const layer l, network net) +{ + if(l.reverse){ + upsample_gpu(l.delta_gpu, l.out_w, l.out_h, l.c, l.batch, l.stride, 1, l.scale, net.delta_gpu); + }else{ + upsample_gpu(net.delta_gpu, l.w, l.h, l.c, l.batch, l.stride, 0, l.scale, l.delta_gpu); + } +} +#endif diff --git a/image.darknet/inst/include/darknet/src/upsample_layer.h b/image.darknet/inst/include/darknet/src/upsample_layer.h new file mode 100644 index 0000000..86790d1 --- /dev/null +++ b/image.darknet/inst/include/darknet/src/upsample_layer.h @@ -0,0 +1,15 @@ +#ifndef UPSAMPLE_LAYER_H +#define UPSAMPLE_LAYER_H +#include "darknet.h" + +layer make_upsample_layer(int batch, int w, int h, int c, int stride); +void forward_upsample_layer(const layer l, network net); +void backward_upsample_layer(const layer l, network net); +void resize_upsample_layer(layer *l, int w, int h); + +#ifdef GPU +void forward_upsample_layer_gpu(const layer l, network net); +void backward_upsample_layer_gpu(const layer l, network net); +#endif + +#endif diff --git a/image.darknet/inst/include/darknet/src/utils.c b/image.darknet/inst/include/darknet/src/utils.c index b5181d7..626b467 100644 --- a/image.darknet/inst/include/darknet/src/utils.c +++ b/image.darknet/inst/include/darknet/src/utils.c @@ -6,9 +6,56 @@ #include #include #include +#include +#include #include "utils.h" + +/* +// old timing. is it better? who knows!! +double get_wall_time() +{ + struct timeval time; + if (gettimeofday(&time,NULL)){ + return 0; + } + return (double)time.tv_sec + (double)time.tv_usec * .000001; +} +*/ + +double what_time_is_it_now() +{ + struct timeval time; + if (gettimeofday(&time,NULL)){ + return 0; + } + return (double)time.tv_sec + (double)time.tv_usec * .000001; +} + +int *read_intlist(char *gpu_list, int *ngpus, int d) +{ + int *gpus = 0; + if(gpu_list){ + int len = strlen(gpu_list); + *ngpus = 1; + int i; + for(i = 0; i < len; ++i){ + if (gpu_list[i] == ',') ++*ngpus; + } + gpus = calloc(*ngpus, sizeof(int)); + for(i = 0; i < *ngpus; ++i){ + gpus[i] = atoi(gpu_list); + gpu_list = strchr(gpu_list, ',')+1; + } + } else { + gpus = calloc(1, sizeof(float)); + *gpus = d; + *ngpus = 1; + } + return gpus; +} + int *read_map(char *filename) { int n = 0; @@ -47,6 +94,22 @@ void shuffle(void *arr, size_t n, size_t size) } } +int *random_index_order(int min, int max) +{ + int *inds = calloc(max-min, sizeof(int)); + int i; + for(i = min; i < max; ++i){ + inds[i] = i; + } + for(i = min; i < max-1; ++i){ + int swap = inds[i]; + int index = i + rand()%(max-i); + inds[i] = inds[index]; + inds[index] = swap; + } + return inds; +} + void del_arg(int argc, char **argv, int index) { int i; @@ -194,6 +257,21 @@ void error(const char *s) exit(-1); } +unsigned char *read_file(char *filename) +{ + FILE *fp = fopen(filename, "rb"); + size_t size; + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + unsigned char *text = calloc(size+1, sizeof(char)); + fread(text, 1, size, fp); + fclose(fp); + return text; +} + void malloc_error() { fprintf(stderr, "Malloc error\n"); @@ -524,6 +602,20 @@ int sample_array(float *a, int n) return n-1; } +int max_int_index(int *a, int n) +{ + if(n <= 0) return -1; + int i, max_i = 0; + int max = a[0]; + for(i = 1; i < n; ++i){ + if(a[i] > max){ + max = a[i]; + max_i = i; + } + } + return max_i; +} + int max_index(float *a, int n) { if(n <= 0) return -1; @@ -538,6 +630,15 @@ int max_index(float *a, int n) return max_i; } +int int_index(int *a, int val, int n) +{ + int i; + for(i = 0; i < n; ++i){ + if(a[i] == val) return i; + } + return -1; +} + int rand_int(int min, int max) { if (max < min){ @@ -585,13 +686,13 @@ float rand_normal() size_t rand_size_t() { return ((size_t)(rand()&0xff) << 56) | - ((size_t)(rand()&0xff) << 48) | - ((size_t)(rand()&0xff) << 40) | - ((size_t)(rand()&0xff) << 32) | - ((size_t)(rand()&0xff) << 24) | - ((size_t)(rand()&0xff) << 16) | - ((size_t)(rand()&0xff) << 8) | - ((size_t)(rand()&0xff) << 0); + ((size_t)(rand()&0xff) << 48) | + ((size_t)(rand()&0xff) << 40) | + ((size_t)(rand()&0xff) << 32) | + ((size_t)(rand()&0xff) << 24) | + ((size_t)(rand()&0xff) << 16) | + ((size_t)(rand()&0xff) << 8) | + ((size_t)(rand()&0xff) << 0); } float rand_uniform(float min, float max) diff --git a/image.darknet/inst/include/darknet/src/utils.h b/image.darknet/inst/include/darknet/src/utils.h index bbc6765..ef24da7 100644 --- a/image.darknet/inst/include/darknet/src/utils.h +++ b/image.darknet/inst/include/darknet/src/utils.h @@ -2,16 +2,22 @@ #define UTILS_H #include #include +#include "darknet.h" #include "list.h" -#define SECRET_NUM -1234 -#define TWO_PI 6.2831853071795864769252866 +#define TIME(a) \ + do { \ + double start = what_time_is_it_now(); \ + a; \ + printf("%s took: %f seconds\n", #a, what_time_is_it_now() - start); \ + } while (0) -int *read_map(char *filename); +#define TWO_PI 6.2831853071795864769252866f + +double what_time_is_it_now(); void shuffle(void *arr, size_t n, size_t size); void sorta_shuffle(void *arr, size_t n, size_t size, size_t sections); void free_ptrs(void **ptrs, int n); -char *basecfg(char *cfgfile); int alphanum_to_int(char c); char int_to_alphanum(int i); int read_int(int fd); @@ -21,44 +27,27 @@ void write_all(int fd, char *buffer, size_t bytes); int read_all_fail(int fd, char *buffer, size_t bytes); int write_all_fail(int fd, char *buffer, size_t bytes); void find_replace(char *str, char *orig, char *rep, char *output); -void error(const char *s); void malloc_error(); void file_error(char *s); void strip(char *s); void strip_char(char *s, char bad); -void top_k(float *a, int n, int k, int *index); list *split_str(char *s, char delim); char *fgetl(FILE *fp); list *parse_csv_line(char *line); char *copy_string(char *s); int count_fields(char *line); float *parse_fields(char *line, int n); -void normalize_array(float *a, int n); -void scale_array(float *a, int n, float s); void translate_array(float *a, int n, float s); -int max_index(float *a, int n); float constrain(float min, float max, float a); int constrain_int(int a, int min, int max); -float mse_array(float *a, int n); -float rand_normal(); -size_t rand_size_t(); -float rand_uniform(float min, float max); float rand_scale(float s); int rand_int(int min, int max); -float sum_array(float *a, int n); -float mean_array(float *a, int n); void mean_arrays(float **a, int n, int els, float *avg); -float variance_array(float *a, int n); -float mag_array(float *a, int n); float dist_array(float *a, float *b, int n, int sub); float **one_hot_encode(float *a, int n, int k); float sec(clock_t clocks); -int find_int_arg(int argc, char **argv, char *arg, int def); -float find_float_arg(int argc, char **argv, char *arg, float def); -int find_arg(int argc, char* argv[], char *arg); -char *find_char_arg(int argc, char **argv, char *arg, char *def); -int sample_array(float *a, int n); void print_statistics(float *a, int n); +int int_index(int *a, int val, int n); #endif diff --git a/image.darknet/inst/include/darknet/src/yolo_layer.c b/image.darknet/inst/include/darknet/src/yolo_layer.c new file mode 100644 index 0000000..c338036 --- /dev/null +++ b/image.darknet/inst/include/darknet/src/yolo_layer.c @@ -0,0 +1,374 @@ +#include "yolo_layer.h" +#include "activations.h" +#include "blas.h" +#include "box.h" +#include "cuda.h" +#include "utils.h" + +#include +#include +#include +#include + +layer make_yolo_layer(int batch, int w, int h, int n, int total, int *mask, int classes) +{ + int i; + layer l = {0}; + l.type = YOLO; + + l.n = n; + l.total = total; + l.batch = batch; + l.h = h; + l.w = w; + l.c = n*(classes + 4 + 1); + l.out_w = l.w; + l.out_h = l.h; + l.out_c = l.c; + l.classes = classes; + l.cost = calloc(1, sizeof(float)); + l.biases = calloc(total*2, sizeof(float)); + if(mask) l.mask = mask; + else{ + l.mask = calloc(n, sizeof(int)); + for(i = 0; i < n; ++i){ + l.mask[i] = i; + } + } + l.bias_updates = calloc(n*2, sizeof(float)); + l.outputs = h*w*n*(classes + 4 + 1); + l.inputs = l.outputs; + l.truths = 90*(4 + 1); + l.delta = calloc(batch*l.outputs, sizeof(float)); + l.output = calloc(batch*l.outputs, sizeof(float)); + for(i = 0; i < total*2; ++i){ + l.biases[i] = .5; + } + + l.forward = forward_yolo_layer; + l.backward = backward_yolo_layer; +#ifdef GPU + l.forward_gpu = forward_yolo_layer_gpu; + l.backward_gpu = backward_yolo_layer_gpu; + l.output_gpu = cuda_make_array(l.output, batch*l.outputs); + l.delta_gpu = cuda_make_array(l.delta, batch*l.outputs); +#endif + + fprintf(stderr, "yolo\n"); + srand(0); + + return l; +} + +void resize_yolo_layer(layer *l, int w, int h) +{ + l->w = w; + l->h = h; + + l->outputs = h*w*l->n*(l->classes + 4 + 1); + l->inputs = l->outputs; + + l->output = realloc(l->output, l->batch*l->outputs*sizeof(float)); + l->delta = realloc(l->delta, l->batch*l->outputs*sizeof(float)); + +#ifdef GPU + cuda_free(l->delta_gpu); + cuda_free(l->output_gpu); + + l->delta_gpu = cuda_make_array(l->delta, l->batch*l->outputs); + l->output_gpu = cuda_make_array(l->output, l->batch*l->outputs); +#endif +} + +box get_yolo_box(float *x, float *biases, int n, int index, int i, int j, int lw, int lh, int w, int h, int stride) +{ + box b; + b.x = (i + x[index + 0*stride]) / lw; + b.y = (j + x[index + 1*stride]) / lh; + b.w = exp(x[index + 2*stride]) * biases[2*n] / w; + b.h = exp(x[index + 3*stride]) * biases[2*n+1] / h; + return b; +} + +float delta_yolo_box(box truth, float *x, float *biases, int n, int index, int i, int j, int lw, int lh, int w, int h, float *delta, float scale, int stride) +{ + box pred = get_yolo_box(x, biases, n, index, i, j, lw, lh, w, h, stride); + float iou = box_iou(pred, truth); + + float tx = (truth.x*lw - i); + float ty = (truth.y*lh - j); + float tw = log(truth.w*w / biases[2*n]); + float th = log(truth.h*h / biases[2*n + 1]); + + delta[index + 0*stride] = scale * (tx - x[index + 0*stride]); + delta[index + 1*stride] = scale * (ty - x[index + 1*stride]); + delta[index + 2*stride] = scale * (tw - x[index + 2*stride]); + delta[index + 3*stride] = scale * (th - x[index + 3*stride]); + return iou; +} + + +void delta_yolo_class(float *output, float *delta, int index, int class, int classes, int stride, float *avg_cat) +{ + int n; + if (delta[index]){ + delta[index + stride*class] = 1 - output[index + stride*class]; + if(avg_cat) *avg_cat += output[index + stride*class]; + return; + } + for(n = 0; n < classes; ++n){ + delta[index + stride*n] = ((n == class)?1 : 0) - output[index + stride*n]; + if(n == class && avg_cat) *avg_cat += output[index + stride*n]; + } +} + +static int entry_index(layer l, int batch, int location, int entry) +{ + int n = location / (l.w*l.h); + int loc = location % (l.w*l.h); + return batch*l.outputs + n*l.w*l.h*(4+l.classes+1) + entry*l.w*l.h + loc; +} + +void forward_yolo_layer(const layer l, network net) +{ + int i,j,b,t,n; + memcpy(l.output, net.input, l.outputs*l.batch*sizeof(float)); + +#ifndef GPU + for (b = 0; b < l.batch; ++b){ + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + activate_array(l.output + index, 2*l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, 4); + activate_array(l.output + index, (1+l.classes)*l.w*l.h, LOGISTIC); + } + } +#endif + + memset(l.delta, 0, l.outputs * l.batch * sizeof(float)); + if(!net.train) return; + float avg_iou = 0; + float recall = 0; + float recall75 = 0; + float avg_cat = 0; + float avg_obj = 0; + float avg_anyobj = 0; + int count = 0; + int class_count = 0; + *(l.cost) = 0; + for (b = 0; b < l.batch; ++b) { + for (j = 0; j < l.h; ++j) { + for (i = 0; i < l.w; ++i) { + for (n = 0; n < l.n; ++n) { + int box_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 0); + box pred = get_yolo_box(l.output, l.biases, l.mask[n], box_index, i, j, l.w, l.h, net.w, net.h, l.w*l.h); + float best_iou = 0; + int best_t = 0; + for(t = 0; t < l.max_boxes; ++t){ + box truth = float_to_box(net.truth + t*(4 + 1) + b*l.truths, 1); + if(!truth.x) break; + float iou = box_iou(pred, truth); + if (iou > best_iou) { + best_iou = iou; + best_t = t; + } + } + int obj_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 4); + avg_anyobj += l.output[obj_index]; + l.delta[obj_index] = 0 - l.output[obj_index]; + if (best_iou > l.ignore_thresh) { + l.delta[obj_index] = 0; + } + if (best_iou > l.truth_thresh) { + l.delta[obj_index] = 1 - l.output[obj_index]; + + int class = net.truth[best_t*(4 + 1) + b*l.truths + 4]; + if (l.map) class = l.map[class]; + int class_index = entry_index(l, b, n*l.w*l.h + j*l.w + i, 4 + 1); + delta_yolo_class(l.output, l.delta, class_index, class, l.classes, l.w*l.h, 0); + box truth = float_to_box(net.truth + best_t*(4 + 1) + b*l.truths, 1); + delta_yolo_box(truth, l.output, l.biases, l.mask[n], box_index, i, j, l.w, l.h, net.w, net.h, l.delta, (2-truth.w*truth.h), l.w*l.h); + } + } + } + } + for(t = 0; t < l.max_boxes; ++t){ + box truth = float_to_box(net.truth + t*(4 + 1) + b*l.truths, 1); + + if(!truth.x) break; + float best_iou = 0; + int best_n = 0; + i = (truth.x * l.w); + j = (truth.y * l.h); + box truth_shift = truth; + truth_shift.x = truth_shift.y = 0; + for(n = 0; n < l.total; ++n){ + box pred = {0}; + pred.w = l.biases[2*n]/net.w; + pred.h = l.biases[2*n+1]/net.h; + float iou = box_iou(pred, truth_shift); + if (iou > best_iou){ + best_iou = iou; + best_n = n; + } + } + + int mask_n = int_index(l.mask, best_n, l.n); + if(mask_n >= 0){ + int box_index = entry_index(l, b, mask_n*l.w*l.h + j*l.w + i, 0); + float iou = delta_yolo_box(truth, l.output, l.biases, best_n, box_index, i, j, l.w, l.h, net.w, net.h, l.delta, (2-truth.w*truth.h), l.w*l.h); + + int obj_index = entry_index(l, b, mask_n*l.w*l.h + j*l.w + i, 4); + avg_obj += l.output[obj_index]; + l.delta[obj_index] = 1 - l.output[obj_index]; + + int class = net.truth[t*(4 + 1) + b*l.truths + 4]; + if (l.map) class = l.map[class]; + int class_index = entry_index(l, b, mask_n*l.w*l.h + j*l.w + i, 4 + 1); + delta_yolo_class(l.output, l.delta, class_index, class, l.classes, l.w*l.h, &avg_cat); + + ++count; + ++class_count; + if(iou > .5) recall += 1; + if(iou > .75) recall75 += 1; + avg_iou += iou; + } + } + } + *(l.cost) = pow(mag_array(l.delta, l.outputs * l.batch), 2); + printf("Region %d Avg IOU: %f, Class: %f, Obj: %f, No Obj: %f, .5R: %f, .75R: %f, count: %d\n", net.index, avg_iou/count, avg_cat/class_count, avg_obj/count, avg_anyobj/(l.w*l.h*l.n*l.batch), recall/count, recall75/count, count); +} + +void backward_yolo_layer(const layer l, network net) +{ + axpy_cpu(l.batch*l.inputs, 1, l.delta, 1, net.delta, 1); +} + +void correct_yolo_boxes(detection *dets, int n, int w, int h, int netw, int neth, int relative) +{ + int i; + int new_w=0; + int new_h=0; + if (((float)netw/w) < ((float)neth/h)) { + new_w = netw; + new_h = (h * netw)/w; + } else { + new_h = neth; + new_w = (w * neth)/h; + } + for (i = 0; i < n; ++i){ + box b = dets[i].bbox; + b.x = (b.x - (netw - new_w)/2./netw) / ((float)new_w/netw); + b.y = (b.y - (neth - new_h)/2./neth) / ((float)new_h/neth); + b.w *= (float)netw/new_w; + b.h *= (float)neth/new_h; + if(!relative){ + b.x *= w; + b.w *= w; + b.y *= h; + b.h *= h; + } + dets[i].bbox = b; + } +} + +int yolo_num_detections(layer l, float thresh) +{ + int i, n; + int count = 0; + for (i = 0; i < l.w*l.h; ++i){ + for(n = 0; n < l.n; ++n){ + int obj_index = entry_index(l, 0, n*l.w*l.h + i, 4); + if(l.output[obj_index] > thresh){ + ++count; + } + } + } + return count; +} + +void avg_flipped_yolo(layer l) +{ + int i,j,n,z; + float *flip = l.output + l.outputs; + for (j = 0; j < l.h; ++j) { + for (i = 0; i < l.w/2; ++i) { + for (n = 0; n < l.n; ++n) { + for(z = 0; z < l.classes + 4 + 1; ++z){ + int i1 = z*l.w*l.h*l.n + n*l.w*l.h + j*l.w + i; + int i2 = z*l.w*l.h*l.n + n*l.w*l.h + j*l.w + (l.w - i - 1); + float swap = flip[i1]; + flip[i1] = flip[i2]; + flip[i2] = swap; + if(z == 0){ + flip[i1] = -flip[i1]; + flip[i2] = -flip[i2]; + } + } + } + } + } + for(i = 0; i < l.outputs; ++i){ + l.output[i] = (l.output[i] + flip[i])/2.; + } +} + +int get_yolo_detections(layer l, int w, int h, int netw, int neth, float thresh, int *map, int relative, detection *dets) +{ + int i,j,n; + float *predictions = l.output; + if (l.batch == 2) avg_flipped_yolo(l); + int count = 0; + for (i = 0; i < l.w*l.h; ++i){ + int row = i / l.w; + int col = i % l.w; + for(n = 0; n < l.n; ++n){ + int obj_index = entry_index(l, 0, n*l.w*l.h + i, 4); + float objectness = predictions[obj_index]; + if(objectness <= thresh) continue; + int box_index = entry_index(l, 0, n*l.w*l.h + i, 0); + dets[count].bbox = get_yolo_box(predictions, l.biases, l.mask[n], box_index, col, row, l.w, l.h, netw, neth, l.w*l.h); + dets[count].objectness = objectness; + dets[count].classes = l.classes; + for(j = 0; j < l.classes; ++j){ + int class_index = entry_index(l, 0, n*l.w*l.h + i, 4 + 1 + j); + float prob = objectness*predictions[class_index]; + dets[count].prob[j] = (prob > thresh) ? prob : 0; + } + ++count; + } + } + correct_yolo_boxes(dets, count, w, h, netw, neth, relative); + return count; +} + +#ifdef GPU + +void forward_yolo_layer_gpu(const layer l, network net) +{ + copy_gpu(l.batch*l.inputs, net.input_gpu, 1, l.output_gpu, 1); + int b, n; + for (b = 0; b < l.batch; ++b){ + for(n = 0; n < l.n; ++n){ + int index = entry_index(l, b, n*l.w*l.h, 0); + activate_array_gpu(l.output_gpu + index, 2*l.w*l.h, LOGISTIC); + index = entry_index(l, b, n*l.w*l.h, 4); + activate_array_gpu(l.output_gpu + index, (1+l.classes)*l.w*l.h, LOGISTIC); + } + } + if(!net.train || l.onlyforward){ + cuda_pull_array(l.output_gpu, l.output, l.batch*l.outputs); + return; + } + + cuda_pull_array(l.output_gpu, net.input, l.batch*l.inputs); + forward_yolo_layer(l, net); + cuda_push_array(l.delta_gpu, l.delta, l.batch*l.outputs); +} + +void backward_yolo_layer_gpu(const layer l, network net) +{ + axpy_gpu(l.batch*l.inputs, 1, l.delta_gpu, 1, net.delta_gpu, 1); +} +#endif + diff --git a/image.darknet/inst/include/darknet/src/yolo_layer.h b/image.darknet/inst/include/darknet/src/yolo_layer.h new file mode 100644 index 0000000..d2a0243 --- /dev/null +++ b/image.darknet/inst/include/darknet/src/yolo_layer.h @@ -0,0 +1,19 @@ +#ifndef YOLO_LAYER_H +#define YOLO_LAYER_H + +#include "darknet.h" +#include "layer.h" +#include "network.h" + +layer make_yolo_layer(int batch, int w, int h, int n, int total, int *mask, int classes); +void forward_yolo_layer(const layer l, network net); +void backward_yolo_layer(const layer l, network net); +void resize_yolo_layer(layer *l, int w, int h); +int yolo_num_detections(layer l, float thresh); + +#ifdef GPU +void forward_yolo_layer_gpu(const layer l, network net); +void backward_yolo_layer_gpu(layer l, network net); +#endif + +#endif From 8a49e6c9f1c10b4eb83b3971828da9de564f28da Mon Sep 17 00:00:00 2001 From: Jeroen Staab Date: Mon, 24 Dec 2018 16:26:23 +0100 Subject: [PATCH 4/8] Copied file from YOLO repository ./include/darknet.h --- image.darknet/src/darknet.h | 805 ++++++++++++++++++++++++++++++++++++ 1 file changed, 805 insertions(+) create mode 100644 image.darknet/src/darknet.h diff --git a/image.darknet/src/darknet.h b/image.darknet/src/darknet.h new file mode 100644 index 0000000..4390c61 --- /dev/null +++ b/image.darknet/src/darknet.h @@ -0,0 +1,805 @@ +#ifndef DARKNET_API +#define DARKNET_API +#include +#include +#include +#include + +#ifdef GPU + #define BLOCK 512 + + #include "cuda_runtime.h" + #include "curand.h" + #include "cublas_v2.h" + + #ifdef CUDNN + #include "cudnn.h" + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define SECRET_NUM -1234 +extern int gpu_index; + +typedef struct{ + int classes; + char **names; +} metadata; + +metadata get_metadata(char *file); + +typedef struct{ + int *leaf; + int n; + int *parent; + int *child; + int *group; + char **name; + + int groups; + int *group_size; + int *group_offset; +} tree; +tree *read_tree(char *filename); + +typedef enum{ + LOGISTIC, RELU, RELIE, LINEAR, RAMP, TANH, PLSE, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU +} ACTIVATION; + +typedef enum{ + PNG, BMP, TGA, JPG +} IMTYPE; + +typedef enum{ + MULT, ADD, SUB, DIV +} BINARY_ACTIVATION; + +typedef enum { + CONVOLUTIONAL, + DECONVOLUTIONAL, + CONNECTED, + MAXPOOL, + SOFTMAX, + DETECTION, + DROPOUT, + CROP, + ROUTE, + COST, + NORMALIZATION, + AVGPOOL, + LOCAL, + SHORTCUT, + ACTIVE, + RNN, + GRU, + LSTM, + CRNN, + BATCHNORM, + NETWORK, + XNOR, + REGION, + YOLO, + ISEG, + REORG, + UPSAMPLE, + LOGXENT, + L2NORM, + BLANK +} LAYER_TYPE; + +typedef enum{ + SSE, MASKED, L1, SEG, SMOOTH,WGAN +} COST_TYPE; + +typedef struct{ + int batch; + float learning_rate; + float momentum; + float decay; + int adam; + float B1; + float B2; + float eps; + int t; +} update_args; + +struct network; +typedef struct network network; + +struct layer; +typedef struct layer layer; + +struct layer{ + LAYER_TYPE type; + ACTIVATION activation; + COST_TYPE cost_type; + void (*forward) (struct layer, struct network); + void (*backward) (struct layer, struct network); + void (*update) (struct layer, update_args); + void (*forward_gpu) (struct layer, struct network); + void (*backward_gpu) (struct layer, struct network); + void (*update_gpu) (struct layer, update_args); + int batch_normalize; + int shortcut; + int batch; + int forced; + int flipped; + int inputs; + int outputs; + int nweights; + int nbiases; + int extra; + int truths; + int h,w,c; + int out_h, out_w, out_c; + int n; + int max_boxes; + int groups; + int size; + int side; + int stride; + int reverse; + int flatten; + int spatial; + int pad; + int sqrt; + int flip; + int index; + int binary; + int xnor; + int steps; + int hidden; + int truth; + float smooth; + float dot; + float angle; + float jitter; + float saturation; + float exposure; + float shift; + float ratio; + float learning_rate_scale; + float clip; + int noloss; + int softmax; + int classes; + int coords; + int background; + int rescore; + int objectness; + int joint; + int noadjust; + int reorg; + int log; + int tanh; + int *mask; + int total; + + float alpha; + float beta; + float kappa; + + float coord_scale; + float object_scale; + float noobject_scale; + float mask_scale; + float class_scale; + int bias_match; + int random; + float ignore_thresh; + float truth_thresh; + float thresh; + float focus; + int classfix; + int absolute; + + int onlyforward; + int stopbackward; + int dontload; + int dontsave; + int dontloadscales; + int numload; + + float temperature; + float probability; + float scale; + + char * cweights; + int * indexes; + int * input_layers; + int * input_sizes; + int * map; + int * counts; + float ** sums; + float * rand; + float * cost; + float * state; + float * prev_state; + float * forgot_state; + float * forgot_delta; + float * state_delta; + float * combine_cpu; + float * combine_delta_cpu; + + float * concat; + float * concat_delta; + + float * binary_weights; + + float * biases; + float * bias_updates; + + float * scales; + float * scale_updates; + + float * weights; + float * weight_updates; + + float * delta; + float * output; + float * loss; + float * squared; + float * norms; + + float * spatial_mean; + float * mean; + float * variance; + + float * mean_delta; + float * variance_delta; + + float * rolling_mean; + float * rolling_variance; + + float * x; + float * x_norm; + + float * m; + float * v; + + float * bias_m; + float * bias_v; + float * scale_m; + float * scale_v; + + + float *z_cpu; + float *r_cpu; + float *h_cpu; + float * prev_state_cpu; + + float *temp_cpu; + float *temp2_cpu; + float *temp3_cpu; + + float *dh_cpu; + float *hh_cpu; + float *prev_cell_cpu; + float *cell_cpu; + float *f_cpu; + float *i_cpu; + float *g_cpu; + float *o_cpu; + float *c_cpu; + float *dc_cpu; + + float * binary_input; + + struct layer *input_layer; + struct layer *self_layer; + struct layer *output_layer; + + struct layer *reset_layer; + struct layer *update_layer; + struct layer *state_layer; + + struct layer *input_gate_layer; + struct layer *state_gate_layer; + struct layer *input_save_layer; + struct layer *state_save_layer; + struct layer *input_state_layer; + struct layer *state_state_layer; + + struct layer *input_z_layer; + struct layer *state_z_layer; + + struct layer *input_r_layer; + struct layer *state_r_layer; + + struct layer *input_h_layer; + struct layer *state_h_layer; + + struct layer *wz; + struct layer *uz; + struct layer *wr; + struct layer *ur; + struct layer *wh; + struct layer *uh; + struct layer *uo; + struct layer *wo; + struct layer *uf; + struct layer *wf; + struct layer *ui; + struct layer *wi; + struct layer *ug; + struct layer *wg; + + tree *softmax_tree; + + size_t workspace_size; + +#ifdef GPU + int *indexes_gpu; + + float *z_gpu; + float *r_gpu; + float *h_gpu; + + float *temp_gpu; + float *temp2_gpu; + float *temp3_gpu; + + float *dh_gpu; + float *hh_gpu; + float *prev_cell_gpu; + float *cell_gpu; + float *f_gpu; + float *i_gpu; + float *g_gpu; + float *o_gpu; + float *c_gpu; + float *dc_gpu; + + float *m_gpu; + float *v_gpu; + float *bias_m_gpu; + float *scale_m_gpu; + float *bias_v_gpu; + float *scale_v_gpu; + + float * combine_gpu; + float * combine_delta_gpu; + + float * prev_state_gpu; + float * forgot_state_gpu; + float * forgot_delta_gpu; + float * state_gpu; + float * state_delta_gpu; + float * gate_gpu; + float * gate_delta_gpu; + float * save_gpu; + float * save_delta_gpu; + float * concat_gpu; + float * concat_delta_gpu; + + float * binary_input_gpu; + float * binary_weights_gpu; + + float * mean_gpu; + float * variance_gpu; + + float * rolling_mean_gpu; + float * rolling_variance_gpu; + + float * variance_delta_gpu; + float * mean_delta_gpu; + + float * x_gpu; + float * x_norm_gpu; + float * weights_gpu; + float * weight_updates_gpu; + float * weight_change_gpu; + + float * biases_gpu; + float * bias_updates_gpu; + float * bias_change_gpu; + + float * scales_gpu; + float * scale_updates_gpu; + float * scale_change_gpu; + + float * output_gpu; + float * loss_gpu; + float * delta_gpu; + float * rand_gpu; + float * squared_gpu; + float * norms_gpu; +#ifdef CUDNN + cudnnTensorDescriptor_t srcTensorDesc, dstTensorDesc; + cudnnTensorDescriptor_t dsrcTensorDesc, ddstTensorDesc; + cudnnTensorDescriptor_t normTensorDesc; + cudnnFilterDescriptor_t weightDesc; + cudnnFilterDescriptor_t dweightDesc; + cudnnConvolutionDescriptor_t convDesc; + cudnnConvolutionFwdAlgo_t fw_algo; + cudnnConvolutionBwdDataAlgo_t bd_algo; + cudnnConvolutionBwdFilterAlgo_t bf_algo; +#endif +#endif +}; + +void free_layer(layer); + +typedef enum { + CONSTANT, STEP, EXP, POLY, STEPS, SIG, RANDOM +} learning_rate_policy; + +typedef struct network{ + int n; + int batch; + size_t *seen; + int *t; + float epoch; + int subdivisions; + layer *layers; + float *output; + learning_rate_policy policy; + + float learning_rate; + float momentum; + float decay; + float gamma; + float scale; + float power; + int time_steps; + int step; + int max_batches; + float *scales; + int *steps; + int num_steps; + int burn_in; + + int adam; + float B1; + float B2; + float eps; + + int inputs; + int outputs; + int truths; + int notruth; + int h, w, c; + int max_crop; + int min_crop; + float max_ratio; + float min_ratio; + int center; + float angle; + float aspect; + float exposure; + float saturation; + float hue; + int random; + + int gpu_index; + tree *hierarchy; + + float *input; + float *truth; + float *delta; + float *workspace; + int train; + int index; + float *cost; + float clip; + +#ifdef GPU + float *input_gpu; + float *truth_gpu; + float *delta_gpu; + float *output_gpu; +#endif + +} network; + +typedef struct { + int w; + int h; + float scale; + float rad; + float dx; + float dy; + float aspect; +} augment_args; + +typedef struct { + int w; + int h; + int c; + float *data; +} image; + +typedef struct{ + float x, y, w, h; +} box; + +typedef struct detection{ + box bbox; + int classes; + float *prob; + float *mask; + float objectness; + int sort_class; +} detection; + +typedef struct matrix{ + int rows, cols; + float **vals; +} matrix; + + +typedef struct{ + int w, h; + matrix X; + matrix y; + int shallow; + int *num_boxes; + box **boxes; +} data; + +typedef enum { + CLASSIFICATION_DATA, DETECTION_DATA, CAPTCHA_DATA, REGION_DATA, IMAGE_DATA, COMPARE_DATA, WRITING_DATA, SWAG_DATA, TAG_DATA, OLD_CLASSIFICATION_DATA, STUDY_DATA, DET_DATA, SUPER_DATA, LETTERBOX_DATA, REGRESSION_DATA, SEGMENTATION_DATA, INSTANCE_DATA, ISEG_DATA +} data_type; + +typedef struct load_args{ + int threads; + char **paths; + char *path; + int n; + int m; + char **labels; + int h; + int w; + int out_w; + int out_h; + int nh; + int nw; + int num_boxes; + int min, max, size; + int classes; + int background; + int scale; + int center; + int coords; + float jitter; + float angle; + float aspect; + float saturation; + float exposure; + float hue; + data *d; + image *im; + image *resized; + data_type type; + tree *hierarchy; +} load_args; + +typedef struct{ + int id; + float x,y,w,h; + float left, right, top, bottom; +} box_label; + + +network *load_network(char *cfg, char *weights, int clear); +load_args get_base_args(network *net); + +void free_data(data d); + +typedef struct node{ + void *val; + struct node *next; + struct node *prev; +} node; + +typedef struct list{ + int size; + node *front; + node *back; +} list; + +pthread_t load_data(load_args args); +list *read_data_cfg(char *filename); +list *read_cfg(char *filename); +unsigned char *read_file(char *filename); +data resize_data(data orig, int w, int h); +data *tile_data(data orig, int divs, int size); +data select_data(data *orig, int *inds); + +void forward_network(network *net); +void backward_network(network *net); +void update_network(network *net); + + +float dot_cpu(int N, float *X, int INCX, float *Y, int INCY); +void axpy_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY); +void copy_cpu(int N, float *X, int INCX, float *Y, int INCY); +void scal_cpu(int N, float ALPHA, float *X, int INCX); +void fill_cpu(int N, float ALPHA, float * X, int INCX); +void normalize_cpu(float *x, float *mean, float *variance, int batch, int filters, int spatial); +void softmax(float *input, int n, float temp, int stride, float *output); + +int best_3d_shift_r(image a, image b, int min, int max); +#ifdef GPU +void axpy_gpu(int N, float ALPHA, float * X, int INCX, float * Y, int INCY); +void fill_gpu(int N, float ALPHA, float * X, int INCX); +void scal_gpu(int N, float ALPHA, float * X, int INCX); +void copy_gpu(int N, float * X, int INCX, float * Y, int INCY); + +void cuda_set_device(int n); +void cuda_free(float *x_gpu); +float *cuda_make_array(float *x, size_t n); +void cuda_pull_array(float *x_gpu, float *x, size_t n); +float cuda_mag_array(float *x_gpu, size_t n); +void cuda_push_array(float *x_gpu, float *x, size_t n); + +void forward_network_gpu(network *net); +void backward_network_gpu(network *net); +void update_network_gpu(network *net); + +float train_networks(network **nets, int n, data d, int interval); +void sync_nets(network **nets, int n, int interval); +void harmless_update_network_gpu(network *net); +#endif +image get_label(image **characters, char *string, int size); +void draw_label(image a, int r, int c, image label, const float *rgb); +void save_image(image im, const char *name); +void save_image_options(image im, const char *name, IMTYPE f, int quality); +void get_next_batch(data d, int n, int offset, float *X, float *y); +void grayscale_image_3c(image im); +void normalize_image(image p); +void matrix_to_csv(matrix m); +float train_network_sgd(network *net, data d, int n); +void rgbgr_image(image im); +data copy_data(data d); +data concat_data(data d1, data d2); +data load_cifar10_data(char *filename); +float matrix_topk_accuracy(matrix truth, matrix guess, int k); +void matrix_add_matrix(matrix from, matrix to); +void scale_matrix(matrix m, float scale); +matrix csv_to_matrix(char *filename); +float *network_accuracies(network *net, data d, int n); +float train_network_datum(network *net); +image make_random_image(int w, int h, int c); + +void denormalize_connected_layer(layer l); +void denormalize_convolutional_layer(layer l); +void statistics_connected_layer(layer l); +void rescale_weights(layer l, float scale, float trans); +void rgbgr_weights(layer l); +image *get_weights(layer l); + +void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int frame_skip, char *prefix, int avg, float hier_thresh, int w, int h, int fps, int fullscreen); +void get_detection_detections(layer l, int w, int h, float thresh, detection *dets); + +char *option_find_str(list *l, char *key, char *def); +int option_find_int(list *l, char *key, int def); +int option_find_int_quiet(list *l, char *key, int def); + +network *parse_network_cfg(char *filename); +void save_weights(network *net, char *filename); +void load_weights(network *net, char *filename); +void save_weights_upto(network *net, char *filename, int cutoff); +void load_weights_upto(network *net, char *filename, int start, int cutoff); + +void zero_objectness(layer l); +void get_region_detections(layer l, int w, int h, int netw, int neth, float thresh, int *map, float tree_thresh, int relative, detection *dets); +int get_yolo_detections(layer l, int w, int h, int netw, int neth, float thresh, int *map, int relative, detection *dets); +void free_network(network *net); +void set_batch_network(network *net, int b); +void set_temp_network(network *net, float t); +image load_image(char *filename, int w, int h, int c); +image load_image_color(char *filename, int w, int h); +image make_image(int w, int h, int c); +image resize_image(image im, int w, int h); +void censor_image(image im, int dx, int dy, int w, int h); +image letterbox_image(image im, int w, int h); +image crop_image(image im, int dx, int dy, int w, int h); +image center_crop_image(image im, int w, int h); +image resize_min(image im, int min); +image resize_max(image im, int max); +image threshold_image(image im, float thresh); +image mask_to_rgb(image mask); +int resize_network(network *net, int w, int h); +void free_matrix(matrix m); +void test_resize(char *filename); +int show_image(image p, const char *name, int ms); +image copy_image(image p); +void draw_box_width(image a, int x1, int y1, int x2, int y2, int w, float r, float g, float b); +float get_current_rate(network *net); +void composite_3d(char *f1, char *f2, char *out, int delta); +data load_data_old(char **paths, int n, int m, char **labels, int k, int w, int h); +size_t get_current_batch(network *net); +void constrain_image(image im); +image get_network_image_layer(network *net, int i); +layer get_network_output_layer(network *net); +void top_predictions(network *net, int n, int *index); +void flip_image(image a); +image float_to_image(int w, int h, int c, float *data); +void ghost_image(image source, image dest, int dx, int dy); +float network_accuracy(network *net, data d); +void random_distort_image(image im, float hue, float saturation, float exposure); +void fill_image(image m, float s); +image grayscale_image(image im); +void rotate_image_cw(image im, int times); +double what_time_is_it_now(); +image rotate_image(image m, float rad); +void visualize_network(network *net); +float box_iou(box a, box b); +data load_all_cifar10(); +box_label *read_boxes(char *filename, int *n); +box float_to_box(float *f, int stride); +void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes); + +matrix network_predict_data(network *net, data test); +image **load_alphabet(); +image get_network_image(network *net); +float *network_predict(network *net, float *input); + +int network_width(network *net); +int network_height(network *net); +float *network_predict_image(network *net, image im); +void network_detect(network *net, image im, float thresh, float hier_thresh, float nms, detection *dets); +detection *get_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, int *num); +void free_detections(detection *dets, int n); + +void reset_network_state(network *net, int b); + +char **get_labels(char *filename); +void do_nms_obj(detection *dets, int total, int classes, float thresh); +void do_nms_sort(detection *dets, int total, int classes, float thresh); + +matrix make_matrix(int rows, int cols); + +#ifdef OPENCV +void *open_video_stream(const char *f, int c, int w, int h, int fps); +image get_image_from_stream(void *p); +void make_window(char *name, int w, int h, int fullscreen); +#endif + +void free_image(image m); +float train_network(network *net, data d); +pthread_t load_data_in_thread(load_args args); +void load_data_blocking(load_args args); +list *get_paths(char *filename); +void hierarchy_predictions(float *predictions, int n, tree *hier, int only_leaves, int stride); +void change_leaves(tree *t, char *leaf_list); + +int find_int_arg(int argc, char **argv, char *arg, int def); +float find_float_arg(int argc, char **argv, char *arg, float def); +int find_arg(int argc, char* argv[], char *arg); +char *find_char_arg(int argc, char **argv, char *arg, char *def); +char *basecfg(char *cfgfile); +void find_replace(char *str, char *orig, char *rep, char *output); +void free_ptrs(void **ptrs, int n); +char *fgetl(FILE *fp); +void strip(char *s); +float sec(clock_t clocks); +void **list_to_array(list *l); +void top_k(float *a, int n, int k, int *index); +int *read_map(char *filename); +void error(const char *s); +int max_index(float *a, int n); +int max_int_index(int *a, int n); +int sample_array(float *a, int n); +int *random_index_order(int min, int max); +void free_list(list *l); +float mse_array(float *a, int n); +float variance_array(float *a, int n); +float mag_array(float *a, int n); +void scale_array(float *a, int n, float s); +float mean_array(float *a, int n); +float sum_array(float *a, int n); +void normalize_array(float *a, int n); +int *read_intlist(char *s, int *n, int d); +size_t rand_size_t(); +float rand_normal(); +float rand_uniform(float min, float max); + +#ifdef __cplusplus +} +#endif +#endif From ba658f9af6d9e393b2136fde194e69df34982fd5 Mon Sep 17 00:00:00 2001 From: Jeroen Staab Date: Mon, 24 Dec 2018 16:26:47 +0100 Subject: [PATCH 5/8] Debugged using code lines from detector.c --- image.darknet/src/__R_API_detector.c | 32 +++++++---------- image.darknet/src/compare.c | 54 +++++++++++----------------- 2 files changed, 34 insertions(+), 52 deletions(-) diff --git a/image.darknet/src/__R_API_detector.c b/image.darknet/src/__R_API_detector.c index 905e582..b0b0f1b 100644 --- a/image.darknet/src/__R_API_detector.c +++ b/image.darknet/src/__R_API_detector.c @@ -32,11 +32,9 @@ image **load_alphabet_pkg(char *path) int darknet_test_detector(char *cfgfile, char *weightfile, char *filename, float thresh, float hier_thresh, char **names, char *path) { image **alphabet = load_alphabet_pkg(path); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + + set_batch_network(net, 1); srand(2222222); clock_t time; char buff[256]; @@ -47,8 +45,9 @@ int darknet_test_detector(char *cfgfile, char *weightfile, char *filename, float while(1){ strncpy(input, filename, 256); image im = load_image_color(input,0,0); - image sized = resize_image(im, net.w, net.h); - layer l = net.layers[net.n-1]; + + image sized = letterbox_image(im, net->w, net->h); + layer l = net->layers[net->n-1]; box *boxes = calloc(l.w*l.h*l.n, sizeof(box)); float **probs = calloc(l.w*l.h*l.n, sizeof(float *)); @@ -58,20 +57,15 @@ int darknet_test_detector(char *cfgfile, char *weightfile, char *filename, float time=clock(); network_predict(net, X); printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); - get_region_boxes(l, 1, 1, thresh, probs, boxes, 0, 0, hier_thresh); - if (l.softmax_tree && nms) do_nms_obj(boxes, probs, l.w*l.h*l.n, l.classes, nms); - else if (nms) do_nms_sort(boxes, probs, l.w*l.h*l.n, l.classes, nms); - for(int i = 0; i < l.w*l.h*l.n; ++i){ - int class = max_index(probs[i], l.classes); - float prob = probs[i][class]; - if(prob > thresh){ - boxes_abovethreshold = boxes_abovethreshold + 1; - } - } + int nboxes = 0; + detection *dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, 0, 1, &nboxes); + //printf("%d\n", 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); + free_detections(dets, nboxes); - printf("Boxes: %d of which %d above the threshold.\n", l.w*l.h*l.n, boxes_abovethreshold); - draw_detections(im, l.w*l.h*l.n, thresh, boxes, probs, names, alphabet, l.classes); save_image(im, "predictions"); free_image(im); diff --git a/image.darknet/src/compare.c b/image.darknet/src/compare.c index d2d2b3b..ef1de6c 100644 --- a/image.darknet/src/compare.c +++ b/image.darknet/src/compare.c @@ -14,10 +14,8 @@ void train_compare(char *cfgfile, char *weightfile) char *base = basecfg(cfgfile); char *backup_directory = "/home/pjreddie/backup/"; printf("%s\n", base); - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } + network net = *load_network(cfgfile, weightfile, 0); + printf("Learning Rate: %g, Momentum: %g, Decay: %g\n", net.learning_rate, net.momentum, net.decay); int imgs = 1024; list *plist = get_paths("data/compare.train.list"); @@ -51,7 +49,7 @@ void train_compare(char *cfgfile, char *weightfile) load_thread = load_data_in_thread(args); printf("Loaded: %lf seconds\n", sec(clock()-time)); time=clock(); - float loss = train_network(net, train); + float loss = train_network(&net, train); if(avg_loss == -1) avg_loss = loss; avg_loss = avg_loss*.9 + loss*.1; printf("%.3f: %f, %f avg, %lf seconds, %ld images\n", (float)*net.seen/N, loss, avg_loss, sec(clock()-time), *net.seen); @@ -59,32 +57,29 @@ void train_compare(char *cfgfile, char *weightfile) if(i%100 == 0){ char buff[256]; sprintf(buff, "%s/%s_%d_minor_%d.weights",backup_directory,base, epoch, i); - save_weights(net, buff); + save_weights(&net, buff); } if(*net.seen/N > epoch){ epoch = *net.seen/N; i = 0; char buff[256]; sprintf(buff, "%s/%s_%d.weights",backup_directory,base, epoch); - save_weights(net, buff); + save_weights(&net, buff); if(epoch%22 == 0) net.learning_rate *= .1; } } pthread_join(load_thread, 0); free_data(buffer); - free_network(net); + free_network(&net); free_ptrs((void**)paths, plist->size); free_list(plist); free(base); } -void validate_compare(char *filename, char *weightfile) +void validate_compare(char *cfgfile, char *weightfile) { int i = 0; - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } + network net = *load_network(cfgfile, weightfile, 0); srand(time(0)); list *plist = get_paths("data/compare.val.list"); @@ -127,7 +122,7 @@ void validate_compare(char *filename, char *weightfile) printf("Loaded: %d images in %lf seconds\n", val.X.rows, sec(clock()-time)); time=clock(); - matrix pred = network_predict_data(net, val); + matrix pred = network_predict_data(&net, val); int j,k; for(j = 0; j < val.y.rows; ++j){ for(k = 0; k < 20; ++k){ @@ -179,7 +174,7 @@ int bbox_comparator(const void *a, const void *b) float *X = calloc(net.w*net.h*net.c, sizeof(float)); memcpy(X, im1.data, im1.w*im1.h*im1.c*sizeof(float)); memcpy(X+im1.w*im1.h*im1.c, im2.data, im2.w*im2.h*im2.c*sizeof(float)); - float *predictions = network_predict(net, X); + float *predictions = network_predict(&net, X); free_image(im1); free_image(im2); @@ -208,7 +203,7 @@ void bbox_fight(network net, sortable_bbox *a, sortable_bbox *b, int classes, in float *X = calloc(net.w*net.h*net.c, sizeof(float)); memcpy(X, im1.data, im1.w*im1.h*im1.c*sizeof(float)); memcpy(X+im1.w*im1.h*im1.c, im2.data, im2.w*im2.h*im2.c*sizeof(float)); - float *predictions = network_predict(net, X); + float *predictions = network_predict(&net, X); ++total_compares; int i; @@ -224,15 +219,12 @@ void bbox_fight(network net, sortable_bbox *a, sortable_bbox *b, int classes, in free(X); } -void SortMaster3000(char *filename, char *weightfile) +void SortMaster3000(char *cfgfile, char *weightfile) { int i = 0; - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, 0); srand(time(0)); - set_batch_network(&net, 1); + set_batch_network(net, 1); list *plist = get_paths("data/compare.sort.list"); //list *plist = get_paths("data/compare.val.old"); @@ -243,7 +235,7 @@ void SortMaster3000(char *filename, char *weightfile) printf("Sorting %d boxes...\n", N); for(i = 0; i < N; ++i){ boxes[i].filename = paths[i]; - boxes[i].net = net; + boxes[i].net = *net; boxes[i].class = 7; boxes[i].elo = 1500; } @@ -255,17 +247,13 @@ void SortMaster3000(char *filename, char *weightfile) printf("Sorted in %d compares, %f secs\n", total_compares, sec(clock()-time)); } -void BattleRoyaleWithCheese(char *filename, char *weightfile) +void BattleRoyaleWithCheese(char *cfgfile, char *weightfile) { int classes = 20; int i,j; - network net = parse_network_cfg(filename); - if(weightfile){ - load_weights(&net, weightfile); - } + network *net = load_network(cfgfile, weightfile, 0); srand(time(0)); - set_batch_network(&net, 1); - + set_batch_network(net, 1); list *plist = get_paths("data/compare.sort.list"); //list *plist = get_paths("data/compare.small.list"); //list *plist = get_paths("data/compare.cat.list"); @@ -278,7 +266,7 @@ void BattleRoyaleWithCheese(char *filename, char *weightfile) printf("Battling %d boxes...\n", N); for(i = 0; i < N; ++i){ boxes[i].filename = paths[i]; - boxes[i].net = net; + boxes[i].net = *net; boxes[i].classes = classes; boxes[i].elos = calloc(classes, sizeof(float));; for(j = 0; j < classes; ++j){ @@ -292,7 +280,7 @@ void BattleRoyaleWithCheese(char *filename, char *weightfile) printf("Round: %d\n", round); shuffle(boxes, N, sizeof(sortable_bbox)); for(i = 0; i < N/2; ++i){ - bbox_fight(net, boxes+i*2, boxes+i*2+1, classes, -1); + bbox_fight(*net, boxes+i*2, boxes+i*2+1, classes, -1); } printf("Round: %f secs, %d remaining\n", sec(clock()-round_time), N); } @@ -312,7 +300,7 @@ void BattleRoyaleWithCheese(char *filename, char *weightfile) sorta_shuffle(boxes, N, sizeof(sortable_bbox), 10); for(i = 0; i < N/2; ++i){ - bbox_fight(net, boxes+i*2, boxes+i*2+1, classes, class); + bbox_fight(*net, boxes+i*2, boxes+i*2+1, classes, class); } qsort(boxes, N, sizeof(sortable_bbox), elo_comparator); if(round <= 20) N = (N*9/10)/2*2; From 2ca7f7379615c11387fa1030dad0e17e2682b2ad Mon Sep 17 00:00:00 2001 From: Jeroen Staab Date: Mon, 24 Dec 2018 16:55:23 +0100 Subject: [PATCH 6/8] Successfully updated package to work with yolov3 --- image.darknet/R/yolo_detect.R | 4 ++-- image.darknet/man/image_darknet_detect.Rd | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/image.darknet/R/yolo_detect.R b/image.darknet/R/yolo_detect.R index 98ea2eb..42ce291 100644 --- a/image.darknet/R/yolo_detect.R +++ b/image.darknet/R/yolo_detect.R @@ -32,8 +32,8 @@ #' weights <- file.path(system.file(package="image.darknet", "models"), "yolo.weights") #' download.file(url = "http://pjreddie.com/media/files/yolo.weights", destfile = weights) #' yolo_coco <- image_darknet_model(type = 'detect', -#' model = "yolo.cfg", -#' weights = system.file(package="image.darknet", "models", "yolo.weights"), +#' model = "yolov3.cfg", +#' weights = system.file(package="image.darknet", "models", "yolov3.weights"), #' labels = system.file(package="image.darknet", "include", "darknet", "data", "coco.names")) #' yolo_coco #' diff --git a/image.darknet/man/image_darknet_detect.Rd b/image.darknet/man/image_darknet_detect.Rd index 91b0cd3..52f770b 100644 --- a/image.darknet/man/image_darknet_detect.Rd +++ b/image.darknet/man/image_darknet_detect.Rd @@ -45,8 +45,8 @@ x <- image_darknet_detect(file = f, object = yolo_tiny_voc) weights <- file.path(system.file(package="image.darknet", "models"), "yolo.weights") download.file(url = "http://pjreddie.com/media/files/yolo.weights", destfile = weights) yolo_coco <- image_darknet_model(type = 'detect', - model = "yolo.cfg", - weights = system.file(package="image.darknet", "models", "yolo.weights"), + model = "yolov3.cfg", + weights = system.file(package="image.darknet", "models", "yolov3.weights"), labels = system.file(package="image.darknet", "include", "darknet", "data", "coco.names")) yolo_coco From 4327b83d70e7e36b52023e0dca9704d98092a0e0 Mon Sep 17 00:00:00 2001 From: Jeroen Staab Date: Wed, 26 Dec 2018 04:16:15 +0100 Subject: [PATCH 7/8] Successfully updated package to work with yolov3 --- image.darknet/inst/models/.gitignore | 1 + image.darknet/src/__R_API_classifier.c | 15 ++++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) create mode 100644 image.darknet/inst/models/.gitignore diff --git a/image.darknet/inst/models/.gitignore b/image.darknet/inst/models/.gitignore new file mode 100644 index 0000000..7e26728 --- /dev/null +++ b/image.darknet/inst/models/.gitignore @@ -0,0 +1 @@ +yolov3.weights diff --git a/image.darknet/src/__R_API_classifier.c b/image.darknet/src/__R_API_classifier.c index cfffa5a..4e88201 100644 --- a/image.darknet/src/__R_API_classifier.c +++ b/image.darknet/src/__R_API_classifier.c @@ -22,11 +22,8 @@ image get_image_from_stream(CvCapture *cap); void darknet_predict_classifier(char *datacfg, char *cfgfile, char *weightfile, char *filename, int top, char **pred_lab, double *pred_score, char **names, int resize){ - network net = parse_network_cfg(cfgfile); - if(weightfile){ - load_weights(&net, weightfile); - } - set_batch_network(&net, 1); + network *net = load_network(cfgfile, weightfile, 0); + set_batch_network(net, 1); srand(2222222); /* list *options = read_data_cfg(datacfg); @@ -41,21 +38,21 @@ void darknet_predict_classifier(char *datacfg, char *cfgfile, char *weightfile, int *indexes = calloc(top, sizeof(int)); char buff[256]; char *input = buff; - int size = net.w; + int size = net->w; while(1){ strncpy(input, filename, 256); image im = load_image_color(input, 0, 0); image r = resize_min(im, size); if(resize > 0) { - resize_network(&net, r.w, r.h); + resize_network(net, r.w, r.h); } //printf("%d %d\n", r.w, r.h); float *X = r.data; time=clock(); float *predictions = network_predict(net, X); - if(net.hierarchy) hierarchy_predictions(predictions, net.outputs, net.hierarchy, 0); - top_k(predictions, net.outputs, top, indexes); + if(net->hierarchy) hierarchy_predictions(predictions, net->outputs, net->hierarchy, 0, 1); + top_k(predictions, net->outputs, top, indexes); //printf("%s: Predicted in %f seconds.\n", input, sec(clock()-time)); for(i = 0; i < top; ++i){ int index = indexes[i]; From f40bb4ecf4e7623d9146ebd5dbfc11b27e583eee Mon Sep 17 00:00:00 2001 From: Jee Roen Date: Fri, 28 Dec 2018 20:21:02 +0100 Subject: [PATCH 8/8] Delete Makefile.bak --- .../inst/include/darknet/Makefile.bak | 82 ------------------- 1 file changed, 82 deletions(-) delete mode 100644 image.darknet/inst/include/darknet/Makefile.bak diff --git a/image.darknet/inst/include/darknet/Makefile.bak b/image.darknet/inst/include/darknet/Makefile.bak deleted file mode 100644 index 3d3d5e4..0000000 --- a/image.darknet/inst/include/darknet/Makefile.bak +++ /dev/null @@ -1,82 +0,0 @@ -GPU=0 -CUDNN=0 -OPENCV=0 -DEBUG=0 - -ARCH= -gencode arch=compute_20,code=[sm_20,sm_21] \ - -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] - -# This is what I use, uncomment if you know your arch and want to specify -# ARCH= -gencode arch=compute_52,code=compute_52 - -VPATH=./src/ -EXEC=darknet -OBJDIR=./obj/ - -CC=gcc -NVCC=nvcc -OPTS=-Ofast -LDFLAGS= -lm -pthread -COMMON= -CFLAGS=-Wall -Wfatal-errors - -ifeq ($(DEBUG), 1) -OPTS=-O0 -g -endif - -CFLAGS+=$(OPTS) - -ifeq ($(OPENCV), 1) -COMMON+= -DOPENCV -CFLAGS+= -DOPENCV -LDFLAGS+= `pkg-config --libs opencv` -COMMON+= `pkg-config --cflags opencv` -endif - -ifeq ($(GPU), 1) -COMMON+= -DGPU -I/usr/local/cuda/include/ -CFLAGS+= -DGPU -LDFLAGS+= -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand -endif - -ifeq ($(CUDNN), 1) -COMMON+= -DCUDNN -CFLAGS+= -DCUDNN -LDFLAGS+= -lcudnn -endif - -OBJ=gemm.o utils.o cuda.o convolutional_layer.o list.o image.o activations.o im2col.o col2im.o blas.o crop_layer.o dropout_layer.o maxpool_layer.o softmax_layer.o data.o matrix.o network.o connected_layer.o cost_layer.o parser.o option_list.o darknet.o detection_layer.o captcha.o route_layer.o writing.o box.o nightmare.o normalization_layer.o avgpool_layer.o coco.o dice.o yolo.o detector.o layer.o compare.o classifier.o local_layer.o swag.o shortcut_layer.o activation_layer.o rnn_layer.o gru_layer.o rnn.o rnn_vid.o crnn_layer.o demo.o tag.o cifar.o go.o batchnorm_layer.o art.o region_layer.o reorg_layer.o super.o voxel.o tree.o -ifeq ($(GPU), 1) -LDFLAGS+= -lstdc++ -OBJ+=convolutional_kernels.o activation_kernels.o im2col_kernels.o col2im_kernels.o blas_kernels.o crop_layer_kernels.o dropout_layer_kernels.o maxpool_layer_kernels.o network_kernels.o avgpool_layer_kernels.o -endif - -OBJS = $(addprefix $(OBJDIR), $(OBJ)) -DEPS = $(wildcard src/*.h) Makefile - -all: obj backup results $(EXEC) - -$(EXEC): $(OBJS) - $(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS) - -$(OBJDIR)%.o: %.c $(DEPS) - $(CC) $(COMMON) $(CFLAGS) -c $< -o $@ - -$(OBJDIR)%.o: %.cu $(DEPS) - $(NVCC) $(ARCH) $(COMMON) --compiler-options "$(CFLAGS)" -c $< -o $@ - -obj: - mkdir -p obj -backup: - mkdir -p backup -results: - mkdir -p results - -.PHONY: clean - -clean: - rm -rf $(OBJS) $(EXEC) -