目錄
1. 僅輸出指定類別的檢測結果
coco模型,只輸出人、車或其他指定類別的檢測結果
這個比較簡單,只需要在打印框、輸出框信息的時候加個判斷即可。
修改src/image.c中的draw_detection函數
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");//判斷標籤是否爲person
is_car=!strcmp(labelstr,"car");
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));
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);
}
}
https://blog.csdn.net/u012244950/article/details/25143019,C++裁剪圖片
https://blog.csdn.net/weixin_40003920/article/details/79693221。裁剪yolo
https://blog.csdn.net/LOVE1055259415/article/details/81017075,裁剪yolo
2. 裁剪出目標
只打印指定類別的檢測結果+裁剪出目標,修改src/image.c中的draw_detection函數。
void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes)
{
int i,j;
for(i = 0; i < num; ++i){
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);
}
else if (dets[i].prob[j] > 0)
{
printf("Error %s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
}
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 = 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;
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");
if(Is_person || Is_car){
IplImage* src;
src=image_to_iplImage(im,src);
// cvShowImage("src",src);
int x = left*1.0;
int y = top*1.0;
int w = (right - left)*1.0;
int h = (bot - top)*1.0;
cvSetImageROI(src,cvRect(x,y,w,h)); //這裏如果寬或者高越界會導致錯誤,閃退/
IplImage* crop = cvCreateImage(cvSize(w,h),IPL_DEPTH_8U,src->nChannels);
cvCopy(src,crop,0);
cvResetImageROI(src);
draw_box_width(im, left, top, right, bot, width, red, green, blue);
save_image(im,"test"); //這裏的圖像已經是有框的
src = image_to_iplImage(im,src);
cvShowImage("src_bbox",src);
cvShowImage("crop",crop);
cvSaveImage("crop.jpg",crop,0);
if (alphabet) {
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);
}
}
}
}
}
3. 添加目標置信度
在src/image.c中修改函數draw_detections函數。
strcat函數:用來連接字符串,其原型爲:char *strcat(char *dest, const char *src);dest 爲目的字符串指針,src 爲源字符串指針。將參數 src 字符串複製到參數 dest 所指的字符串尾部;dest 最後的結束字符 NULL 會被覆蓋掉,並在連接後的字符串的尾部再增加一個 NULL。返回dest 字符串起始地址。
修改image.c文件
大概239行,draw_detector函數,源代碼如下:
void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes)
{
int i,j;
for(i = 0; i < num; ++i){
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);
}
else if (dets[i].prob[j] > 0)
{
printf("Error %s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
}
......
}
修改後,代碼如下:
void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes)
{
int i,j;
for(i = 0; i < num; ++i){
char labelstr[4096] = {0};
int class = -1;
char lprob[10];//修改的部分
for(j = 0; j < classes; ++j){
sprintf(lprob, "%.2f",dets[i].prob[j]*100);//修改的部分
if (dets[i].prob[j] > thresh){
if (class < 0) {
strcat(labelstr, names[j]);
strcat(labelstr, ": ");//修改的部分
strcat(labelstr, lprob);//修改的部分
strcat(labelstr, "%");//修改的部分
class = j;
} else {
strcat(labelstr, ", ");
strcat(labelstr, names[j]);
strcat(labelstr, ": ");//修改的部分
strcat(labelstr, lprob);//修改的部分
strcat(labelstr, "%");//修改的部分
}
printf("%s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
else if (dets[i].prob[j] > 0)
{
printf("Error %s: %.0f%%\n", names[j], dets[i].prob[j]*100);
}
}
......
}
這樣make之後,測試圖片便顯示了置信度。
4. 批量測試圖片並保存至指定文件夾
打開darknet文件下examples/detector.c文件,替換test_detector函數如下:
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;
int i=0;
while(1){
if(filename){//如果直接輸入單張路徑,則進行單張圖片測試 和源代碼相同
strncpy(input, filename, 256);
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
cvNamedWindow("predictions", CV_WINDOW_NORMAL);
if(fullscreen){
cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
}
show_image(im, "predictions");
cvWaitKey(0);
cvDestroyAllWindows();
#endif
}
free_image(im);
free_image(sized);
if (filename) break;
}
else { //如果命令行無圖片路徑,則提示輸入txt路徑,txt文件包含圖片的路徑
printf("Enter Image Path: ");
fflush(stdout);
input = fgets(input, 256, stdin);
if(!input) return;
strtok(input, "\n");
list *plist = get_paths(input);//這裏同train_detector時讀取圖片的操作一樣
char **paths = (char **)list_to_array(plist);
printf("Start Testing!\n");
int m = plist->size;
//access()函數,確定文件的訪問權限 access(filename,mode)
//mode=0,判斷文件是否存在;mode=1,檢測文件是否可運行;mode=2,檢測文件是否可寫訪問;mode=4,檢查是//否可讀訪問;mode=6,檢查是否可讀/寫訪問
//返回0表示存在,返回-1表示不存在
if(access("/xxx/darknet/data/out",0)==-1)//"/xxx/darknet/data"修改成自己的路徑,想把測試結果存哪就寫哪
{
//如果沒有上述文件,則用mkdir創建,若創建失敗,返回-1.
if (mkdir("/xxx/darknet/data/out",0777))//"/xxx/darknet/data"修改成自己的路徑
{
printf("create file bag failed!!!");
}
}
for(i = 0; i < m; ++i){//m張圖片
char *path = paths[i];
image im = load_image_color(path,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("Try Very Hard:");
printf("%s: Predicted in %f seconds.\n", path, 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{
char b[2048];
sprintf(b,"/xxx/darknet/data/out/%s",GetFilename(path));//"/xxx/darknet/data"修改成自己的路徑
save_image(im, b);
printf("save %s successfully!\n",GetFilename(path));
#ifdef OPENCV
cvNamedWindow("predictions", CV_WINDOW_NORMAL);
if(fullscreen){
cvSetWindowProperty("predictions", CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
}
show_image(im, "predictions");
cvWaitKey(0);
cvDestroyAllWindows();
#endif
}
free_image(im);
free_image(sized);
if (filename) break;
}
}
}
}
在前面添加函數:
#include "darknet.h"
#include <sys/stat.h>
#include<stdio.h>
#include<time.h>
#include<sys/types.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};
char *GetFilename(char *p)
{
static char name[20]={""};
char *q = strrchr(p,'/') + 1;
strncpy(name,q,6);
return name;
}
在darknet下重新編譯,make.
終端輸入測試指令:
./darknet detector test cfg/xx.data cfg/xx.cfg backup/xx.weights
加載完模型後出現Enter Image Path:
這個時候,輸入test.txt的路徑。test.txt即放測試圖片路徑的文檔。之後測試結果就保存在test_detector函數中寫的路徑下的out文件夾內。
5. 保存視頻檢測結果
./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights <video file>,測試視頻指令
https://blog.csdn.net/sinat_33718563/article/details/79964758,保存視頻測試結果。
6. 修改模型保存頻率
在examples文件夾中的detector.c文件中,if(i%10000==0 || (i < 1000 && i%100 == 0)) 表示迭代次數1000以下,每一百次保存一次模型,1000次以上,每1W次保存一次模型,可以根據需要自行修改。
if(i%100==0){//這裏改的是每多少次保存一個back模型,這個會覆蓋掉之前的back模型
#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%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);
}
修改文件後,要重新編譯make,才能生效。如果不行,就先make clean,再make。
7. 保存訓練日誌,參數可視化
訓練指令後面加2>1 | tee train_log.txt(後面這個txt名自定義,放在darknet文件下,記錄訓練日誌),tee保存網絡加載信息和checkout點保存信息。
./darknet detector train cfg/xx.data cfg/xx.cfg backup/xx.weights 2>1 | tee train_log.txt
日誌參數可視化:
https://blog.csdn.net/yudiemiaomiao/article/details/72469135,親測可用。三個文件在電腦上,先用convert_log.txt文件,分割log.txt成兩個txt文檔。之後分別運行log.py和iou.py文件(記得修改文件裏面的txt路徑及名稱),可得到loss,avg loss變化圖。(按照原來的x%10==9,loss只出現圖,沒有線。改成和iou一樣,if(x%10==0 or x%10==9)就好了,不造咋回事,沒細究。)