1. 程式人生 > >yolo v2物體分類工程的前期影象預處理關鍵程式碼解析

yolo v2物體分類工程的前期影象預處理關鍵程式碼解析

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比例大小: