안녕하세요, 나무토끼 입니다.
새로운 프로젝트를 위해서 물체인식에 대해서 알아보려고 합니다.
영상 처리가 필요했기 때문에 OpenCV , 딥러닝 여러 단어들을 검색하다 우연히 발견하게 된
YOLO v3 , 이것은 다른 딥러닝 기술보다 빠르다고 하고,,, 원리나 내용은 우선 넘어갈게요.
https://github.com/pjreddie/darknet
위의 주소에서 다운로드를 받을 수 있습니다.
웹 캡을 이용해 실시간으로 물체인식을 수행하는 것도 가능하내요!
원리나 관련된 기술은 나중에 하는 것으로 하겠습니다.
1. 소스 ?
YOLO v3는 C언어로 되어 있습니다. 컴파일한 실행파일에 학습 시킨 cfg 파일과 이미지 파일을 입력하면 ,
현재 280개 정도의 아이템이 인식이 되는것 같습니다.
사람을 인식 하고 싶어서 돌려보니, 몸이 반만 나와도 인식이 되는 듯 합니다.
1-1 인식 전 사진 :
1-2 인식 후 사진 :
가깝고 물체가 큰 사진들은 대부분 인식이 잘되는 듯 합니다.
2. 컴파일 소스 분석 :
Makefile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | GPU=0 CUDNN=0 OPENCV=0 OPENMP=0 DEBUG=0 ARCH= -gencode arch=compute_30,code=sm_30 \ -gencode arch=compute_35,code=sm_35 \ -gencode arch=compute_50,code=[sm_50,compute_50] \ -gencode arch=compute_52,code=[sm_52,compute_52] # -gencode arch=compute_20,code=[sm_20,sm_21] \ This one is deprecated? # This is what I use, uncomment if you know your arch and want to specify # ARCH= -gencode arch=compute_52,code=compute_52 VPATH=./src/:./examples SLIB=libdarknet.so ALIB=libdarknet.a EXEC=darknet OBJDIR=./obj/ CC=gcc CPP=g++ NVCC=nvcc AR=ar ARFLAGS=rcs OPTS=-Ofast LDFLAGS= -lm -pthread COMMON= -Iinclude/ -Isrc/ CFLAGS=-Wall -Wno-unused-result -Wno-unknown-pragmas -Wfatal-errors -fPIC | cs |
컴파일을 위한 Makefile을 보자면,
경로 설정이 src 폴더와 examples 폴더로 설정 되어 있내요.
VPATH=./src/:./examples
그리고 아마 int main << 소스 파일이 구동 되는 곳은
EXEC=darknet
./example/darknet.c 에 있을 겁니다.
3. Darknet.c (Main 소스 분석) :
Makefile 을 참고하고 Darknet 소스코드 파일을 찾으니 역시나 메인이 여기 있내요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | int main(int argc, char **argv) { //test_resize("data/bad.jpg"); //test_box(); //test_convolutional_layer(); if(argc < 2){ fprintf(stderr, "usage: %s <function>\n", argv[0]); return 0; } gpu_index = find_int_arg(argc, argv, "-i", 0); if(find_arg(argc, argv, "-nogpu")) { gpu_index = -1; } #ifndef GPU gpu_index = -1; #else if(gpu_index >= 0){ cuda_set_device(gpu_index); } #endif if (0 == strcmp(argv[1], "average")){ average(argc, argv); } else if (0 == strcmp(argv[1], "yolo")){ run_yolo(argc, argv); } else if (0 == strcmp(argv[1], "super")){ run_super(argc, argv); } else if (0 == strcmp(argv[1], "lsd")){ run_lsd(argc, argv); } else if (0 == strcmp(argv[1], "detector")){ run_detector(argc, argv); } else if (0 == strcmp(argv[1], "detect")){ float thresh = find_float_arg(argc, argv, "-thresh", .5); char *filename = (argc > 4) ? argv[4]: 0; char *outfile = find_char_arg(argc, argv, "-out", 0); int fullscreen = find_arg(argc, argv, "-fullscreen"); test_detector("cfg/coco.data", argv[2], argv[3], filename, thresh, .5, outfile, fullscreen); } else if (0 == strcmp(argv[1], "cifar")){ run_cifar(argc, argv); } else if (0 == strcmp(argv[1], "go")){ run_go(argc, argv); } else if (0 == strcmp(argv[1], "rnn")){ run_char_rnn(argc, argv); } else if (0 == strcmp(argv[1], "coco")){ run_coco(argc, argv); } else if (0 == strcmp(argv[1], "classify")){ predict_classifier("cfg/imagenet1k.data", argv[2], argv[3], argv[4], 5); } else if (0 == strcmp(argv[1], "classifier")){ run_classifier(argc, argv); } else if (0 == strcmp(argv[1], "regressor")){ run_regressor(argc, argv); } else if (0 == strcmp(argv[1], "isegmenter")){ run_isegmenter(argc, argv); } else if (0 == strcmp(argv[1], "segmenter")){ run_segmenter(argc, argv); } else if (0 == strcmp(argv[1], "art")){ run_art(argc, argv); } else if (0 == strcmp(argv[1], "tag")){ run_tag(argc, argv); } else if (0 == strcmp(argv[1], "3d")){ composite_3d(argv[2], argv[3], argv[4], (argc > 5) ? atof(argv[5]) : 0); } else if (0 == strcmp(argv[1], "test")){ test_resize(argv[2]); } else if (0 == strcmp(argv[1], "nightmare")){ run_nightmare(argc, argv); } else if (0 == strcmp(argv[1], "rgbgr")){ rgbgr_net(argv[2], argv[3], argv[4]); } else if (0 == strcmp(argv[1], "reset")){ reset_normalize_net(argv[2], argv[3], argv[4]); } else if (0 == strcmp(argv[1], "denormalize")){ denormalize_net(argv[2], argv[3], argv[4]); } else if (0 == strcmp(argv[1], "statistics")){ statistics_net(argv[2], argv[3]); } else if (0 == strcmp(argv[1], "normalize")){ normalize_net(argv[2], argv[3], argv[4]); } else if (0 == strcmp(argv[1], "rescale")){ rescale_net(argv[2], argv[3], argv[4]); } else if (0 == strcmp(argv[1], "ops")){ operations(argv[2]); } else if (0 == strcmp(argv[1], "speed")){ speed(argv[2], (argc > 3 && argv[3]) ? atoi(argv[3]) : 0); } else if (0 == strcmp(argv[1], "oneoff")){ oneoff(argv[2], argv[3], argv[4]); } else if (0 == strcmp(argv[1], "oneoff2")){ oneoff2(argv[2], argv[3], argv[4], atoi(argv[5])); } else if (0 == strcmp(argv[1], "print")){ print_weights(argv[2], argv[3], atoi(argv[4])); } else if (0 == strcmp(argv[1], "partial")){ partial(argv[2], argv[3], argv[4], atoi(argv[5])); } else if (0 == strcmp(argv[1], "average")){ average(argc, argv); } else if (0 == strcmp(argv[1], "visualize")){ visualize(argv[2], (argc > 3) ? argv[3] : 0); } else if (0 == strcmp(argv[1], "mkimg")){ mkimg(argv[2], argv[3], atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), argv[7]); } else if (0 == strcmp(argv[1], "imtest")){ test_resize(argv[2]); } else { fprintf(stderr, "Not an option: %s\n", argv[1]); } return 0; } | cs |
YOLO 공식 홈페이지를 https://pjreddie.com/darknet/yolo/ 보면 단일 이미지 인식 명령어가 이렇게 되어 있습니다.
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
아까 말씀 드린 것처럼 실행파일은 darknet 으로 컴파일 되어 있고
인자에 속하는 것들이 4개가 있습니다.
여기서 볼 것은 1번째와 4번째 인자 입니다.
3-1. detect
이 인자는 어떤 함수를 돌릴 것인지에 대한 인자 같습니다. 공식 홈페이지에서 비디오와 이미지를 인식시킬때의 인자가 다르기 때문입니다.
./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights <video file>
보시면 이미지의 경우 detect / 그리고 동영상의 경우 detector 를 사용해야 합니다.
즉, 첫번째 인자 "detect" 를 조건문에 걸려 돌아가게 하는 함수는 test_detector 입니다.
함수를 찾기 위해 저는 Atom 을 사용하여 접근이 쉽도록 하였습니다.
4. detector.c
test_detector 함수를 찾아보니 detector.c 파일에 존재 했습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 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; while(1){ if(filename){ strncpy(input, filename, 256); } else { printf("Enter Image Path: "); fflush(stdout); input = fgets(input, 256, stdin); if(!input) return; strtok(input, "\n"); } 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 make_window("predictions", 512, 512, 0); show_image(im, "predictions", 0); #endif } free_image(im); free_image(sized); if (filename) break; } } | cs |
물론 이정도 까지만 와서도, 다른 프로젝트에 사용 될 함수나 (단지 이미지 인식만 필요하다면,,,)
인식 프로세스 대해 알 수 있을 것 같습니다.
4-1 filename
아까 말씀 드렸던 4번째 인자가 사진 파일의 경로이며, 이 함수에서 해당 이미지를 가지고 인식 작업을 시작합니다 .
터미널에서 출력하는 문장들을 봤을때
Predicted in %f seconds.
이 문장 전이 이미지를 처리시키는 부분 일 것입니다.
사실 제가 구현하고 있는 프로젝트는 딥러닝 즉, 이미지 처리 부분까지는 들여다 볼 필요가 없습니다.
제가 필요한 함수는 draw_detections 입니다. 이미지를 처리하고 사진에 박스를 뿌려주는 함수 일텐데,
아마 박스가 가르키는 것이 물체일 것이고, 물체를 활용할 수 있는 부분이라고 생각했습니다.
5. draw_detections
저는 여기서 소스 코드를 수정하여 이미지에 원하는 물체만 인식시키고, 원하는 갯수를 얻게끔 수정했습니다.
갯수의 출력과, 인식 물체의 이름을 바꾸기만 했습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | void draw_detections(image im, detection *dets, int num, float thresh, char **names, image **alphabet, int classes) { int i,j; ////////// insert code //////// int offset2 = 0*123457 % classes; float red2 = get_color(2,offset2,classes); float green2 = get_color(1,offset2,classes); float blue2 = get_color(0,offset2,classes); float rgb2[3]; //width = prob*20+2; rgb2[0] = red2; rgb2[1] = green2; rgb2[2] = blue2; char person_c[7]; int person_cnt2 = 0; ////////////////////////////// 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) { /////////////// insert code ///////// if(strcmp(names[j],"person") == 0){ person_cnt2++; strcat(labelstr, "Smoking_person"); }else{ strcat(labelstr, names[j]); } class = j; } else { /////////////// insert code ///////// strcat(labelstr, ", "); if(strcmp(names[j],"person") == 0){ person_cnt2++; strcat(labelstr, "Smoking_person"); }else{ strcat(labelstr, names[j]); } } if(strcmp(names[j],"person") == 0){ printf("%s: %.0f%%\n", "Smoking_person", dets[i].prob[j]*100); }else{ printf("%s: %.0f%%\n", names[j], dets[i].prob[j]*100); } /////////////// printf("total smoking person : %d \n", person_cnt2 ); /////////////// } } if(class >= 0){ int width = im.h * .006; //remove except perso if(strcmp(names[class],"person") != 0) continue; /* 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; 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); } } } /////////////// insert code ///////// sprintf(person_c,"%d",person_cnt2); printf("this is nnn %s\n",person_c ); if (alphabet) { image label = get_label(alphabet, person_c, (im.h*.03)); draw_label(im, 0 + im.h * .006, 0, label, rgb2); free_image(label); } } | cs |
다양한 프로젝트에 적용시킬 수 있을 것 같습니다. 물체 인식이 필요한 사진들을 구해
학습을 시키고, 적당한 함수내에 사진에서 얻고 싶은 정보를 저장하고 서버로 전송 해주는 그런 기능들 일 것 입니다.
여기까지 !
나머지 작업이 끝나면 , 총정리와 github 에 올리겠습니다.
감사합니다.
'컴퓨터공학 > 딥러닝' 카테고리의 다른 글
[맥북] 딥러닝 텐서플로 part 2. (0) | 2021.04.17 |
---|---|
[맥북] 딥러닝 텐서플로 part 1. (0) | 2021.04.02 |
[YOLO v3] 물체 인식 Real-Time Object Detection (Deap Learning) Darknet (0) | 2018.10.31 |
[골빈해커의 3분 딥러닝 텐서플로맛] Part 2. 텐서플로 설치 / 주피터 노트북 (0) | 2018.10.30 |
[골빈해커의 3분 딥러닝 텐서플로맛] Part 1. 딥러닝 정의 그리고 텐써플로 (0) | 2018.10.29 |