基於yolo提取車和人的檢測與影象裁剪
這次換一個嚴謹的寫作風格。因為之前在做yolo演算法移植到ros上面,已經成功了,但是實驗室老師又出了做姿勢識別的么蛾子。我真是一萬個。。。。無語了。感覺,對深度學習只有一個簡單的瞭解,現在還在讀很多有關的論文,一邊看cs231n的課程,在自學著入門。但是奈何自己的實戰能力太差,實戰經驗太少了,只是學過那一年的c,很多關於指標的操作什麼的,真的太差了,現在還在教一個小孩c語言,我都不好意思教人家了,決定從這星期開始,每天刷一些c的基礎知識題,真的得好好補補了。
回到正題,環境仍然是ubuntu16.04+cuda9.0+NVIDIA GTX1050+OPENCV3.4.1,首先解決第一個問題,在yolo的基礎上,提取人和車,其他的標籤過濾掉。有兩個解決方法,一個是自己訓練車和人的訓練庫,另一個就是在程式中剔除出人和車以外的標籤。第一個方法原來想做一下,但是看網上真正做過的,可能要訓練一個星期,而且可能需要在伺服器上訓練,因為真的沒做過,時間催的很緊,只能採取第二種方法了,等後面有時間了,一定自己訓練一下,試一下,真正做過才能有話語權。那就來說一下,是怎麼在程式裡面實現的剔除其他標籤的。首先,要明確是在,image.c裡面的draw_detections這個函式裡面去改動。
for(i = 0; i < num; ++i){
char labelstr[4096] = {0};
int class = -1;
for(j = 0; j < classes; ++j){
if (probs[i][j] > thresh){
printf("probs:%f\n", probs[i][j]);
if (class < 0) {
strcat(labelstr, names[j]);
class = j;
} else {
strcat(labelstr, ", ");
strcat(labelstr, names[j]);
}
printf("%s: %.0f%%\n", names[j], probs[i][j]*100);
}
}
在這個for迴圈裡,num是檢測到的物體個數,classes是能夠檢測的物體種類,這裡的classes是80,說明yolov2能檢測80個種類的物體,這裡的for迴圈在篩選概率大於閾值然後輸出概率的類別和大小。
int left = (b.x-b.w/2.)*im.w;
int right = (b.x+b.w/2.)*im.w;
int top = (b.y-b.h/2.)*im.h;
int bot = (b.y+b.h/2.)*im.h;
這裡獲得預檢測區域(bounding box)的位置。下面就該新增我自己的程式。
bool Is_person = false;
bool Is_car = false;
Is_person = !strcmp(labelstr, "person");
Is_car = !strcmp(labelstr, "car");
Is_car = !strcmp(labelstr, "truck");
if(Is_person || Is_car)
{
draw_box_width(im, left, top, right, bot, width, red, green, blue);
if (alphabet) {
image label = get_label(alphabet, labelstr, (im.h*.03)/10);
draw_label(im, top + width, left, label, rgb);
free_image(label);
}//這一部分在給已經畫出框的物體打label,追蹤這裡的draw_label就會發現他的標籤是通過索引拼在一起的
if (masks){
image mask = float_to_image(14, 14, 1, masks[i]);
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);
}
所以,通過這段程式碼就可以篩選出來只是人和車的,因為車的種類有很多,但是我只選出來了car和truck這兩個標籤,後面再看看是否有更多的關於車的標籤。
因為實驗室要求做姿態識別,類似交警的指揮手勢,什麼手勢識別出來是向左轉什麼是向右轉。之前也查閱了一些姿態估計和骨骼識別的資料,但是感覺出來的效果來達到老師們所要求的還有點遠。現在想的是,先在yolo上面把檢測出來的人分割出來,在這個影象上做些其他特徵的識別。比如:輪廓識別。但是這又涉及到背景是否複雜等等。所以還沒有考慮到後面,現在只是,先把人從原始影象中裁剪出來。
因為原始碼對影象的處理是用的一維陣列,所以要想在原影象上裁剪ROI區域我還採用的,先把一維陣列轉化成二維影象,然後再用opencv的函式進行影象裁剪。
這段程式碼在darknet_ros包上改的,因為darknet_ros包和darknet的原始碼還是有一點點不一樣,darknet_ros包檢測動態視訊的時候用的就是draw_detections這個函式,而原始碼裡面,這個函式只是在檢測圖片時用的這個函式。
所以在程式裡面就自己仿照他的寫法把原始碼中的image結構體中存放的一維陣列轉化成二維影象類在image.c裡面定義:
void Image_to_iplimage(image im, IplImage* img){
int x, y, k;
image copy = copy_image(im);
if(copy.c == 3) rgbgr_image(copy);
int step = img->widthStep;
for(y = 0; y < copy.h; ++y){
for(x = 0; x < copy.w; ++x){
for(k= 0; k < copy.c; ++k){
img->imageData[y*step + x*copy.c + k] = (unsigned char)(get_pixel(copy,x,y,k)*255);
}
}
}
}
通過這個函式就可以把一維陣列類轉換成二維影象類。
void draw_detections_1(image im, int num, float thresh, box *boxes, float **probs, float **masks, char **names, image **alphabet, int classes, box *target_box)
{
int i,j;
for(i = 0; i < num; ++i){
char labelstr[4096] = {0};
int class = -1;
for(j = 0; j < classes; ++j){
if (probs[i][j] > thresh){
printf("probs:%f\n", probs[i][j]);
if (class < 0) {
strcat(labelstr, names[j]);
class = j;
} else {
strcat(labelstr, ", ");
strcat(labelstr, names[j]);
}
printf("%s: %.0f%%\n", names[j], probs[i][j]*100);
}
}
/***這裡應該找出站的最近的人進行分割,因為視野當中可能有多個人,no code***/
if(class >= 0){
int width = im.h * .006;
/*
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);
float blue = get_color(0,offset,classes);
float rgb[3];
//width = prob*20+2;
rgb[0] = red;
rgb[1] = green;
rgb[2] = blue;
box b = boxes[i];
int left = (b.x-b.w/2.)*im.w;
int right = (b.x+b.w/2.)*im.w;
int top = (b.y-b.h/2.)*im.h;
int bot = (b.y+b.h/2.)*im.h;
if(left < 0) left = 0;
if(right > im.w-1) right = im.w-1;
if(top < 0) top = 0;
if(bot > im.h-1) bot = im.h-1;
bool Is_person = false;
bool Is_car = false;
Is_person = !strcmp(labelstr, "person");
Is_car = !strcmp(labelstr, "car");
Is_car = !strcmp(labelstr, "truck");
if(Is_person || Is_car)
{
target_box->x = left*1.0;
target_box->y = top*1.0;
target_box->w = (right - left)*1.0;
target_box->h = (bot - top)*1.0;
draw_box_width(im, left, top, right, bot, width, red, green, blue);
if (alphabet) {
image label = get_label(alphabet, labelstr, (im.h*.03)/10);
draw_label(im, top + width, left, label, rgb);
free_image(label);
}
if (masks){
image mask = float_to_image(14, 14, 1, masks[i]);
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);
}
}
}
}
}
因為我們想要得到bounding box的座標區域,所以我們需要draw_detections這個函式把bounding box的引數傳回主函式,所以要加一個引數,又因為,draw_detections這個函式用到很多地方,所以我就仿照draw_detections寫了一個相同的函式,只是多了一個傳回來的引數,void draw_detections_1(image im, int num, float thresh, box *boxes, float **probs, float **masks, char **names, image **alphabet, int classes, box *target_box)。
目前就先寫這樣,後期會陸陸續續更關於yolo演算法本身的原理,只不過可能又要到後面了,最近可能還要做一些影象分割或者骨骼識別的東西。
最後的最後,還是要感謝我的王叔叔,男票大神啊,每次都是跟著他的指導,我才能完成任務,謝謝王叔叔。