darknet原始碼解讀-load_data
這裡的資料載入部分的程式碼由detector.c檔案中train_detector函式中load_data處開始解讀。
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"); //store weights? char *backup_directory = option_find_str(options, "backup", "/backup/"); srand(time(0)); //from /a/b/yolov2.cfg extract yolov2 char *base = basecfg(cfgfile); //network config 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 //create network for every GPU nets[i] = load_network(cfgfile, weightfile, clear); nets[i]->learning_rate *= ngpus; } srand(time(0)); network *net = nets[0]; //subdivisions,why not divide? 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; //the last layer e.g. [region] for yolov2 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; /*n張圖片以及圖片上的truth box會被載入到buffer.X,buffer.y裡面去*/ pthread_t load_thread = load_data(args); .... }
在輸入到load_data函式的args結構中有幾個引數需要關注,args.n它是表示這一次載入的影象的數量,args.m是表示訓練集中影象的總量,args.num_boxes表示一張影象中允許的最大的檢測框的數量。
pthread_t load_data(load_args args) { pthread_t thread; struct load_args *ptr = calloc(1, sizeof(struct load_args)); *ptr = args; //e.g. ptr->path from args.path if(pthread_create(&thread, 0, load_threads, ptr)) error("Thread creation failed"); return thread; }
load_data函式裡面會建立一個load_threads的執行緒,從名字上來理解它是一個用來載入執行緒的執行緒,資料的載入並不是由這個執行緒直接來負責的,它更像是一個數據載入執行緒管理者的角色。
void *load_threads(void *ptr) { int i; load_args args = *(load_args *)ptr; if (args.threads == 0) args.threads = 1; data *out = args.d; int total = args.n; free(ptr); data *buffers = calloc(args.threads, sizeof(data)); pthread_t *threads = calloc(args.threads, sizeof(pthread_t)); for(i = 0; i < args.threads; ++i){ args.d = buffers + i; //why not total/args.threads? args.n = (i+1) * total/args.threads - i * total/args.threads; threads[i] = load_data_in_thread(args); } //waiting for thread to load data for(i = 0; i < args.threads; ++i){ pthread_join(threads[i], 0); } *out = concat_datas(buffers, args.threads); out->shallow = 0; for(i = 0; i < args.threads; ++i){ buffers[i].shallow = 1; free_data(buffers[i]); } free(buffers); free(threads); return 0; }
為什麼上面說load_threads是一個數據載入執行緒管理者的角色就是因為在load_data_in_thread中會建立真正負責載入資料的執行緒,load_threads函式內部儲存這些資料載入子執行緒的執行緒id,通過pthread_join函式等待這些子執行緒完成資料載入。建立多少個子執行緒由傳入的args.threads成員決定,因為一次載入的影象的數量是args.n,現在有args.threads個執行緒去完成這項工作,所以分配到單個執行緒的話只需要去載入args.n/args.threads張影象。
pthread_t load_data_in_thread(load_args args)
{
pthread_t thread;
struct load_args *ptr = calloc(1, sizeof(struct load_args));
*ptr = args;
if(pthread_create(&thread, 0, load_thread, ptr)) error("Thread creation failed");
return thread;
}
void *load_thread(void *ptr)
{
//printf("Loading data: %d\n", rand());
load_args a = *(struct load_args*)ptr;
if(a.exposure == 0) a.exposure = 1;
if(a.saturation == 0) a.saturation = 1;
if(a.aspect == 0) a.aspect = 1;
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 == DETECTION_DATA){
//detection data
*a.d = load_data_detection(a.n, a.paths, a.m, a.w, a.h, a.num_boxes, a.classes, a.jitter, a.hue, a.saturation, a.exposure);
}
//...省略...
}
free(ptr);
return 0;
}
load_data_in_thread函式中會建立真正的負責載入資料的子執行緒load_thread並返回執行緒描述符,load_thread會根據要載入的資料型別呼叫相應的函式,我這裡只考慮DETECTION_DATA也就是檢測資料的情形,因此會進一步呼叫load_data_detection函式。
/*
m,total of images
n,part of images for this thread
boxes,max number of boxes per picture
*/
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)
{
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;
//n * boxes * 5(x,y,h,w,score)
d.y = make_matrix(n, 5*boxes);
for(i = 0; i < n; ++i){
image orig = load_image_color(random_paths[i], 0, 0); //load an origin image
image sized = make_image(w, h, orig.c); //make empty image,size is (w,h,c)
fill_image(sized, .5);
float dw = jitter * orig.w;
float dh = jitter * orig.h;
//width to height ratio after jitter
float new_ar = (orig.w + rand_uniform(-dw, dw)) / (orig.h + rand_uniform(-dh, dh));
float scale = rand_uniform(.25, 2);
float nw, nh;
//change w,h but keep the ratio,why?
if(new_ar < 1){
nh = scale * h;
nw = nh * new_ar;
} else {
nw = scale * w;
nh = nw / new_ar;
}
float dx = rand_uniform(0, w - nw);
float dy = rand_uniform(0, h - nh);
place_image(orig, nw, nh, dx, dy, sized);
random_distort_image(sized, hue, saturation, exposure);
//rand flip
int flip = rand()%2;
if(flip) flip_image(sized);
//X is ready
d.X.vals[i] = sized.data;
//y is ready
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(random_paths);
return d;
}
這裡的核心是這個n次的for迴圈,每次迴圈都載入一張影象。由load_image_color將影象檔案路徑中的影象載入到image結構中,因為我們要求的尺寸是(w,h)的,所以緊接著通過make_image生成一張(w,h,orig.c)的空白影象。將原始影象進行一定的變換後填充到生成的空白影象中,這裡面對原始影象一系列變換的意義,我至今不甚瞭解。整個函式中還有一個比較需要注意的點就是fill_truth_detection這個函式。
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];
find_replace(path, "images", "labels", labelpath);
find_replace(labelpath, "JPEGImages", "labels", labelpath);
find_replace(labelpath, "raw", "labels", labelpath);
find_replace(labelpath, ".jpg", ".txt", labelpath);
find_replace(labelpath, ".png", ".txt", labelpath);
find_replace(labelpath, ".JPG", ".txt", labelpath);
find_replace(labelpath, ".JPEG", ".txt", labelpath);
//上面一大堆就是根據資料集目錄結構將圖片路徑變換成labels文件的路徑
int count = 0;
box_label *boxes = read_boxes(labelpath, &count);
//disrupt the box order
randomize_boxes(boxes, count);
//(x,y,w,h)有所調整,所以truth box也要有所糾正
correct_boxes(boxes, count, dx, dy, sx, sy, flip);
if(count > num_boxes) count = num_boxes;
float x,y,w,h;
int id;
int i;
int sub = 0;
//what?
for (i = 0; i < count; ++i) {
x = boxes[i].x;
y = boxes[i].y;
w = boxes[i].w;
h = boxes[i].h;
id = boxes[i].id;
//什麼意思?太小忽略?看樣子是的!
if ((w < .001 || h < .001)) {
++sub;
continue;
}
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);
}
相關推薦
darknet原始碼解讀-load_data
這裡的資料載入部分的程式碼由detector.c檔案中train_detector函式中load_data處開始解讀。 void train_detector(char *datacfg, char *cfgfile, char *weightfile, int *
darknet原始碼解讀-yolov2損失計算
參考文章: yolov2損失計算的原始碼集中在region_layer.c檔案forward_region_layer函式中,為了兼顧座標、分類、目標置信度以及訓練效率,損失函式由多個部分組成,且不同部分都被賦予了各自的損失權重,整體計算公式如下。
darknet原始碼解讀-letterbox_image
letterbox_image對影象調整成輸入尺寸(w,h) //將原圖進行一定比例的縮放,返回的圖片尺寸為(w,h) image letterbox_image(image im, int w, int h) { int new_w = im.w; int
YOLO原始碼(Darknet原始碼)解讀(im2col.c)
#include "im2col.h" #include <stdio.h> // 獲取影象畫素值 float im2col_get_pixel(float *im, int height, int width, int channels,
YOLO原始碼(Darknet原始碼)解讀(convolutional_layer.c)
#include "convolutional_layer.h" #include "utils.h" #include "batchnorm_layer.h" #include "im2col.h" #include "col2im.h" #include "blas.h" #include "g
YOLO原始碼(Darknet原始碼)解讀(layer.c)
#include "layer.h" #include "cuda.h" #include <stdlib.h> void free_layer(layer l) { if(l.type == DROPOUT){ if(l.rand) fr
YOLO原始碼(Darknet原始碼)解讀(network.c)
network.c #include <stdio.h> #include <time.h> #include <assert.h> #include "network.h" #include "image.h" #include "data.h" #inclu
YOLO原始碼(Darknet原始碼)解讀(utils.c)
utils.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <assert.h> #include <u
YOLO原始碼(Darknet原始碼)解讀(yolo.c)
// 將檢測的boxes結果寫入檔案 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
yolo v2 損失函式原始碼解讀
前提說明: 1, 關於 yolo 和 yolo v2 的詳細解釋請移步至如下兩個連結,或者直接看論文(我自己有想寫 yolo 的教程,但思前想後下面兩個連結中的文章質量實在是太好了_(:з」∠)_) yo
【React原始碼解讀】- 元件的實現
前言 react使用也有一段時間了,大家對這個框架褒獎有加,但是它究竟好在哪裡呢? 讓我們結合它的原始碼,探究一二!(當前原始碼為react16,讀者要對react有一定的瞭解) 回到最初 根據react官網上的例子,快速構建react專案 npx create-react-app
【1】pytorch torchvision原始碼解讀之Alexnet
最近開始學習一個新的深度學習框架PyTorch。 框架中有一個非常重要且好用的包:torchvision,顧名思義這個包主要是關於計算機視覺cv的。這個包主要由3個子包組成,分別是:torchvision.datasets、torchvision.models、torchvision.trans
Set介面_HashSet常用方法_JDK原始碼解讀
Set 介面繼承自 Collection ,Set 沒有新增方法,方法和 Collection 保持一致, Set 容器的特點:無序,不可重複,無序指Set 中的元素沒有索引,我們只能遍歷查詢,不重複指不允許加入重複的元素,更確切的說,新元素如果和Set 中某個元素通過 equals() 方
vux之x-input使用以及原始碼解讀
前言 近期專案中使用的vux中的input,以及使用自定義校驗規則和動態匹配錯誤提示,有時間記錄下自己的使用經歷和原始碼分析。希望大家多多指正,留言區發表自己寶貴的建議。 詳解 列舉官方文件中常用的幾個屬性的使用方法,程式碼如下 <group ref="group">
react-redux connect原始碼解讀
今天看了下react-redux的原始碼,主要來看下connect的方法 首先找到connect的入口檔案。在src/index.js下找到。對應connect資料夾下的connect.js檔案。 大致說下原始碼connect流程 connect.js對外暴露是通過ex
java原始碼解讀之HashMap
1:首先下載openjdk(http://pan.baidu.com/s/1dFMZXg1),把原始碼匯入eclipse,以便看到jdk原始碼 Windows-Prefe
以太坊原始碼解讀(5)BlockChain類的解析及NewBlockChain()分析
一、blockchain的資料結構 type BlockChain struct { chainConfig *params.ChainConfig // 初始化配置 cacheConfig *CacheConfig // 快取配置 db ethdb.Databas
以太坊原始碼解讀(4)Block類及其儲存
一、Block類 type Block struct { /******header*******/ header *Header /******header*******/ /******body*********/ uncle
Hystrix之@EnableCircuitBreaker原始碼解讀
Hystrix是一個供分散式系統使用,提供延遲和容錯功能,保證複雜的分佈系統在面臨不可避免的失敗時,仍能有其彈性。 比如系統中有很多服務,當某些服務不穩定的時候,使用這些服務的使用者執行緒將會阻塞,如果沒有隔離機制,系統隨時就有可能會掛掉,從而帶來很大的風險。 SpringCloud使用Hy
String的valueOf方法原始碼解讀
valueOf 中的祕密 String中的valueOf方法大致可以分為三種: String.valueOf(Object)、String.valueOf(char[])、String.valueOf(基本資料型別) 案例: Integer arg = null; St