yolo v2物體分類工程的前期影象預處理關鍵程式碼解析
阿新 • • 發佈:2019-01-23
1、其物體分類工程的樣本增強功能很強大,比caffe的好很多,下面是對訓練工程的樣本預處理程式碼進行解析,
其程式碼入口在data.c檔案,其程式碼如下:
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 i; matrix X; X.rows = n; X.vals = calloc(X.rows, sizeof(float*)); X.cols = 0; 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); //如果開啟了多執行緒,當有多條執行緒搶佔著個資源時,會報錯。 解決方法就是,使用單batch=1即可。 show_image(im, "orig"); show_image(crop, "crop"); cvWaitKey(0); //這裡是進行左右翻轉,不需要配置檔案制定 int flip = random_gen()%2; if (flip) flip_image(crop); //這裡是進行資料樣本的色調、飽和度、曝光度的增強,其值儘可能設定小點。 random_distort_image(crop, hue, saturation, exposure); free_image(im); X.vals[i] = crop.data; X.cols = crop.h*crop.w*crop.c; } return X; }
其中random_augment_image()是進行樣本增強的函式入口,其程式碼位置在image.c,程式碼如下:
image random_augment_image(image im, float angle, float aspect, int low, int high, int size) { aspect = rand_scale(aspect); //這裡的high的值是low的兩倍,這個值或許有點大了,裁剪是單邊裁剪。這裡是把影象按照網路輸入大小進行 //放大操作,讓後在這裡面擷取輸入網路大小的區域,由於r的隨意性,則這個函式起到隨意裁剪影象的左右。 int r = rand_int(low, high); int min = (im.h < im.w*aspect) ? im.h : im.w*aspect; //這裡假設樣本已經被歸一化為w=h大小的樣本了。如果要使用w,h不相等的樣本,則需要修改程式碼,分別計算 //scalew,scaleh大小。 float scale = (float)r / min; //這裡是進行仿射變化的角度值 float rad = rand_uniform(-angle, angle) * TWO_PI / 360.; //float rad = 0 * 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; dx = rand_uniform(-dx, dx); dy = rand_uniform(-dy, dy); //dx = 0; //dy = 0; //這個函式進行旋轉和單邊裁剪。 image crop = rotate_crop_image(im, rad, scale, size, size, dx, dy, aspect); return crop; }
其中的rotate_crop_image()的程式碼位置在image.c,其程式碼如下:
image rotate_crop_image(image im, float rad, float s, int w, int h, float dx, float dy, float aspect) { int x, y, c; float cx = im.w/2.; float cy = im.h/2.; image rot = make_image(w, h, im.c); for(c = 0; c < im.c; ++c){ for(y = 0; y < h; ++y){ for(x = 0; x < w; ++x){ //這裡假設現有的座標x,y是旋轉後的座標,需要求出rx,ry是原始的座標,其值可能是負值,或者大於w,h但是在雙線性插值裡 //進行了判斷,把其限制在適當的範圍,這個就是為什麼可以填補空白影象區域的原因,挺好的。 //(x - w/2.)/s得出來的是樣本的影象座標點,這裡只取了放大影象(r邊長)中間的w,h大小的區域 //所以起到隨意裁剪影象樣本大作用。 float rx = cos(rad)*((x - w/2.)/s*aspect + dx/s*aspect) - sin(rad)*((y - h/2.)/s + dy/s) + cx; float ry = sin(rad)*((x - w/2.)/s*aspect + dx/s*aspect) + cos(rad)*((y - h/2.)/s + dy/s) + cy; //根據原始的座標來進行雙線性插值得出其畫素值,很妙。 float val = bilinear_interpolate(im, rx, ry, c); set_pixel(rot, x, y, c, val); } } } return rot; }
樣本例子展示:
a、這個是進行雙線性插值後,填補無畫素值的效果
b、使用的是w、h軸都不同比例的結果,並且沒有假設影象被放大,而是網路輸入等比例:
c、使用1724x724大小的樣本,在沒有任何裁剪和縮放的情況下,這種不完全情況,這是由於樣本的x,y軸都是採用相同的scale比例大小: