DarkNet 시리즈 - Demo
demo
parameter
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
#define DEMO 1
#ifdef OPENCV
static char **demo_names;
static image **demo_alphabet;
static int demo_classes;
static network *net;
static image buff [3];
static image buff_letter[3];
static int buff_index = 0;
static void * cap;
static float fps = 0;
static float demo_thresh = 0;
static float demo_hier = .5;
static int running = 0;
static int demo_frame = 3;
static int demo_index = 0;
static float **predictions;
static float *avg;
static int demo_done = 0;
static int demo_total = 0;
double demo_time;
detection *get_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, int *num);
- demo_names: 클래스 이름을 담고 있는 문자열 배열
- demo_alphabet: 이미지 출력 시 사용되는 폰트 이미지 배열
- demo_classes: 클래스의 개수
- net: 딥러닝 모델을 담고 있는 네트워크 구조체
- buff: 카메라 또는 비디오 스트림에서 읽어들인 이미지를 담고 있는 이미지 배열
- buff_letter: 이미지 출력 시 사용되는 글자 이미지 배열
- buff_index: 현재 사용 중인 이미지 배열의 인덱스
- cap: 카메라 또는 비디오 스트림을 담고 있는 포인터
- fps: 현재 프레임 속도
- demo_thresh: 객체 탐지에 사용되는 임계값
- demo_hier: 객체 탐지 시 사용되는 IoU 임계값
- running: 프로그램이 실행 중인지 나타내는 플래그
- demo_frame: 현재 프레임 인덱스
- demo_index: 현재 클래스 인덱스
- predictions: 네트워크가 예측한 객체의 정보를 담고 있는 이차원 배열
- avg: 예측한 객체 정보의 평균값
- demo_done: 객체 탐지가 완료되었는지 나타내는 플래그
- demo_total: 객체 탐지된 전체 개수
- demo_time: 객체 탐지에 소요된 시간
함수 이름: detection *get_network_boxes
입력:
- network *net : 사용할 네트워크
- int w : 이미지의 가로 크기
- int h : 이미지의 세로 크기
- float thresh : 객체 검출을 위한 최소 확률 임계값
- float hier : 객체 검출을 위한 최소 IOU 임계값
- int *map : 사용하지 않음
- int relative : 좌표 계산에 사용
- int *num : 검출된 객체의 수를 저장할 포인터
size_network
1
2
3
4
5
6
7
8
9
10
11
12
int size_network(network *net)
{
int i;
int count = 0;
for(i = 0; i < net->n; ++i){
layer l = net->layers[i];
if(l.type == YOLO || l.type == REGION || l.type == DETECTION){
count += l.outputs;
}
}
return count;
}
함수 이름: size_network
입력:
- network 구조체 포인터 (neural network 모델)
동작:
- 입력으로 받은 neural network 모델에서 YOLO, REGION, DETECTION 레이어의 출력 크기를 합산하여 총 출력 크기를 계산한다.
설명:
- 이 함수는 neural network 모델의 출력 크기를 계산하는 함수이다.
- 입력으로 받은 모델의 모든 레이어를 순회하면서 YOLO, REGION, DETECTION 레이어의 출력 크기를 합산하여 반환한다.
- 이 함수는 예를 들어 모델의 출력 크기를 계산하는 데 사용될 수 있으며, 예측이나 추론 결과를 처리하는 데 유용하게 사용될 수 있다.
remember_network
1
2
3
4
5
6
7
8
9
10
11
12
void remember_network(network *net)
{
int i;
int count = 0;
for(i = 0; i < net->n; ++i){
layer l = net->layers[i];
if(l.type == YOLO || l.type == REGION || l.type == DETECTION){
memcpy(predictions[demo_index] + count, net->layers[i].output, sizeof(float) * l.outputs);
count += l.outputs;
}
}
}
함수 이름: remember_network
입력:
- network *net: 뉴럴 네트워크 구조체 포인터
동작:
- 네트워크의 출력 값을 예측 값으로 복사하여 기억합니다.
- 이 함수는 YOLO, REGION 또는 DETECTION 레이어에서 나온 출력 값만 복사합니다.
설명:
- 뉴럴 네트워크에서는 입력 데이터를 이용하여 출력 값을 예측합니다.
- 이 함수는 해당 네트워크의 예측 값을 복사하여 기억합니다.
- 이 기억된 예측 값은 나중에 다양한 목적으로 사용될 수 있습니다.
- 이 함수는 demo_index와 predictions 배열을 사용합니다.
- demo_index는 현재 데모에서 사용되는 이미지의 인덱스를 나타내며, predictions 배열은 예측 값을 기억하기 위한 배열입니다.
- 이 함수는 각 레이어의 출력 값의 크기를 count 변수에 누적하여 predictions 배열의 적절한 위치에 복사합니다.
avg_predictions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
detection *avg_predictions(network *net, int *nboxes)
{
int i, j;
int count = 0;
fill_cpu(demo_total, 0, avg, 1);
for(j = 0; j < demo_frame; ++j){
axpy_cpu(demo_total, 1./demo_frame, predictions[j], 1, avg, 1);
}
for(i = 0; i < net->n; ++i){
layer l = net->layers[i];
if(l.type == YOLO || l.type == REGION || l.type == DETECTION){
memcpy(l.output, avg + count, sizeof(float) * l.outputs);
count += l.outputs;
}
}
detection *dets = get_network_boxes(net, buff[0].w, buff[0].h, demo_thresh, demo_hier, 0, 1, nboxes);
return dets;
}
함수 이름: avg_predictions
입력:
- network *net: 네트워크 모델
- int *nboxes: 감지된 bounding box의 개수를 담을 포인터 변수
동작:
- 이전에 저장된 예측(predictions)을 이용하여 각 클래스에 대한 확률값의 평균을 계산한다.
- 계산된 평균값을 이용하여 다시 네트워크를 실행하고, 감지된 bounding box를 반환한다.
설명:
- 이전에 저장된 predictions은 remember_network 함수를 통해 저장된 예측값을 말한다.
- 이 함수에서는 저장된 예측값들의 평균값을 계산하고, 이를 이용하여 네트워크를 실행한다.
- 이전 예측값들의 평균을 이용함으로써 일시적인 예측값의 변동성을 줄이고, 보다 안정적인 예측 결과를 얻을 수 있다.
- 계산된 예측값을 이용하여 get_network_boxes 함수를 호출하여 bounding box를 감지하고, 이를 반환한다.
detect_in_thread
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
void *detect_in_thread(void *ptr)
{
running = 1;
float nms = .4;
layer l = net->layers[net->n-1];
float *X = buff_letter[(buff_index+2)%3].data;
network_predict(net, X);
/*
if(l.type == DETECTION){
get_detection_boxes(l, 1, 1, demo_thresh, probs, boxes, 0);
} else */
remember_network(net);
detection *dets = 0;
int nboxes = 0;
dets = avg_predictions(net, &nboxes);
/*
int i,j;
box zero = {0};
int classes = l.classes;
for(i = 0; i < demo_detections; ++i){
avg[i].objectness = 0;
avg[i].bbox = zero;
memset(avg[i].prob, 0, classes*sizeof(float));
for(j = 0; j < demo_frame; ++j){
axpy_cpu(classes, 1./demo_frame, dets[j][i].prob, 1, avg[i].prob, 1);
avg[i].objectness += dets[j][i].objectness * 1./demo_frame;
avg[i].bbox.x += dets[j][i].bbox.x * 1./demo_frame;
avg[i].bbox.y += dets[j][i].bbox.y * 1./demo_frame;
avg[i].bbox.w += dets[j][i].bbox.w * 1./demo_frame;
avg[i].bbox.h += dets[j][i].bbox.h * 1./demo_frame;
}
//copy_cpu(classes, dets[0][i].prob, 1, avg[i].prob, 1);
//avg[i].objectness = dets[0][i].objectness;
}
*/
if (nms > 0) do_nms_obj(dets, nboxes, l.classes, nms);
printf("\033[2J");
printf("\033[1;1H");
printf("\nFPS:%.1f\n",fps);
printf("Objects:\n\n");
image display = buff[(buff_index+2) % 3];
draw_detections(display, dets, nboxes, demo_thresh, demo_names, demo_alphabet, demo_classes);
free_detections(dets, nboxes);
demo_index = (demo_index + 1)%demo_frame;
running = 0;
return 0;
}
함수 이름: detect_in_thread
입력:
- void* ptr: 포인터 타입의 인자, 사용하지 않음
동작:
- YOLO 신경망으로 객체를 탐지하고, 결과를 출력 이미지에 표시한다.
- 평균 예측 값을 계산하고, 이를 바탕으로 객체를 탐지하고, 비최대 억제(NMS)를 수행한다.
- 마지막으로, 탐지된 객체들을 이미지에 그리고 출력한다.
설명:
- 먼저, running 변수를 1로 설정하여 스레드가 실행중임을 표시한다.
- nms 변수에 0.4를 할당하여 비최대 억제에 사용될 임계값을 설정한다.
- 다음으로, YOLO 신경망의 출력층(layer)을 가져와서, 현재 처리할 이미지 데이터인 buff_letter[(buff_index+2)%3].data를 입력값으로 전달하여 객체를 예측한다.
- 예측된 결과를 평균 예측 값으로 기억하고, avg_predictions() 함수를 사용하여 평균 예측 값을 바탕으로 객체를 탐지한다.
- 비최대 억제를 수행하여 중복으로 탐지된 객체를 제거한다.
- 마지막으로, 출력 이미지에 탐지된 객체들을 그리고 출력한다. 스레드가 실행을 완료하면 running 변수를 0으로 설정하여 스레드가 종료되었음을 표시한다.
fetch_in_thread
1
2
3
4
5
6
7
8
9
10
11
void *fetch_in_thread(void *ptr)
{
free_image(buff[buff_index]);
buff[buff_index] = get_image_from_stream(cap);
if(buff[buff_index].data == 0) {
demo_done = 1;
return 0;
}
letterbox_image_into(buff[buff_index], net->w, net->h, buff_letter[buff_index]);
return 0;
}
함수 이름: fetch_in_thread
입력:
- void *ptr: 포인터
동작:
- 카메라로부터 이미지를 가져와서 해당 이미지를 신경망 모델의 입력 크기에 맞게 리사이징하고, 이전 이미지를 해제하고 새 이미지로 대체함.
설명:
- 이 함수는 쓰레드에서 실행되며, 카메라 스트림에서 이미지를 가져와서 이전 버퍼 이미지를 해제하고, 새 이미지를 신경망 모델의 입력 크기에 맞게 리사이징하여 현재 버퍼에 할당하는 역할을 합니다.
- 이전에 할당된 이미지 메모리를 해제하여 메모리 누수를 방지합니다.
display_in_thread
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void *display_in_thread(void *ptr)
{
int c = show_image(buff[(buff_index + 1)%3], "Demo", 1);
if (c != -1) c = c%256;
if (c == 27) {
demo_done = 1;
return 0;
} else if (c == 82) {
demo_thresh += .02;
} else if (c == 84) {
demo_thresh -= .02;
if(demo_thresh <= .02) demo_thresh = .02;
} else if (c == 83) {
demo_hier += .02;
} else if (c == 81) {
demo_hier -= .02;
if(demo_hier <= .0) demo_hier = .0;
}
return 0;
}
함수 이름: display_in_thread
입력:
- void *ptr: 포인터
동작:
- 현재 버퍼 중 뒤의 두 번째 이미지를 화면에 표시한다.
- 키보드 입력을 받아 해당하는 기능을 수행한다. (ESC: 종료, R: detection 임계값 증가, F: detection 임계값 감소, T: hierarchy 임계값 증가, G: hierarchy 임계값 감소)
설명:
- 딥러닝 모델이 예측한 결과를 화면에 보여주는 역할을 담당하는 함수이다.
- 버퍼 중 뒤의 두 번째 이미지를 화면에 표시하여 실시간으로 영상을 확인할 수 있도록 한다.
- 키보드 입력을 받아 해당하는 기능을 수행한다. ESC를 누르면 프로그램이 종료되고, R, F, T, G 키를 누르면 각각 detection 임계값을 증가시키거나 감소시키고, hierarchy 임계값을 증가시키거나 감소시킨다.
display_loop
1
2
3
4
5
6
void *display_loop(void *ptr)
{
while(1){
display_in_thread(0);
}
}
함수 이름: display_loop
입력:
- ptr: void 포인터
동작:
- display_in_thread 함수를 무한 루프로 실행하여 영상 출력 창을 유지시키고, 사용자 입력에 따라 demo_thresh와 demo_hier 등의 변수 값을 변경할 수 있도록 한다.
설명:
- 이 함수는 사용자에게 영상 출력 창을 제공하고, 사용자 입력을 받아들여 변수 값들을 조절할 수 있도록 한다.
- display_in_thread 함수는 이 함수 내에서 무한히 반복되며, 영상 출력 창이 종료되거나 프로그램이 종료될 때까지 유지된다.
- 이 함수는 쓰레드로 실행되기 때문에, 메인 프로그램과 별개로 동작하며 영상 출력 창이 종료되더라도 메인 프로그램은 계속해서 실행될 수 있다.
detect_loop
1
2
3
4
5
6
7
void *detect_loop(void *ptr)
{
while(1){
detect_in_thread(0);
}
}
함수 이름: detect_loop
입력:
- ptr: void 포인터 (사용하지 않음)\
동작:
- 무한 루프를 돌면서 detect_in_thread 함수를 호출하여 객체 탐지를 수행\
설명:
- 객체 탐지 루프를 돌며 영상에서 객체를 탐지하고, 그 결과를 화면에 표시하는 함수입니다.
demo
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
void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int delay, char *prefix, int avg_frames, float hier, int w, int h, int frames, int fullscreen)
{
//demo_frame = avg_frames;
image **alphabet = load_alphabet();
demo_names = names;
demo_alphabet = alphabet;
demo_classes = classes;
demo_thresh = thresh;
demo_hier = hier;
printf("Demo\n");
net = load_network(cfgfile, weightfile, 0);
set_batch_network(net, 1);
pthread_t detect_thread;
pthread_t fetch_thread;
srand(2222222);
int i;
demo_total = size_network(net);
predictions = calloc(demo_frame, sizeof(float*));
for (i = 0; i < demo_frame; ++i){
predictions[i] = calloc(demo_total, sizeof(float));
}
avg = calloc(demo_total, sizeof(float));
if(filename){
printf("video file: %s\n", filename);
cap = open_video_stream(filename, 0, 0, 0, 0);
}else{
cap = open_video_stream(0, cam_index, w, h, frames);
}
if(!cap) error("Couldn't connect to webcam.\n");
buff[0] = get_image_from_stream(cap);
buff[1] = copy_image(buff[0]);
buff[2] = copy_image(buff[0]);
buff_letter[0] = letterbox_image(buff[0], net->w, net->h);
buff_letter[1] = letterbox_image(buff[0], net->w, net->h);
buff_letter[2] = letterbox_image(buff[0], net->w, net->h);
int count = 0;
if(!prefix){
make_window("Demo", 1352, 1013, fullscreen);
}
demo_time = what_time_is_it_now();
while(!demo_done){
buff_index = (buff_index + 1) %3;
if(pthread_create(&fetch_thread, 0, fetch_in_thread, 0)) error("Thread creation failed");
if(pthread_create(&detect_thread, 0, detect_in_thread, 0)) error("Thread creation failed");
if(!prefix){
fps = 1./(what_time_is_it_now() - demo_time);
demo_time = what_time_is_it_now();
display_in_thread(0);
}else{
char name[256];
sprintf(name, "%s_%08d", prefix, count);
save_image(buff[(buff_index + 1)%3], name);
}
pthread_join(fetch_thread, 0);
pthread_join(detect_thread, 0);
++count;
}
}
함수 이름: demo
입력:
- char *cfgfile: YOLO 모델의 설정 파일 경로
- char *weightfile: 학습된 YOLO 모델의 가중치 파일 경로
- float thresh: Object detection 결과의 임계값
- int cam_index: 사용할 웹캠의 인덱스 (0부터 시작)
- const char *filename: Object detection을 수행할 동영상 파일 경로 (웹캠을 사용하지 않을 경우에만 사용)
- char **names: Object detection 대상 클래스명 배열
- int classes: Object detection 대상 클래스 수
- int delay: Object detection 프레임 간의 딜레이
- char *prefix: Object detection 결과 저장시 사용할 파일 이름 prefix
- int avg_frames: Object detection 프레임의 평균화 수 (최근 몇 개의 프레임을 평균화하여 Object detection 수행)
- float hier: YOLO 모델의 hier 파라미터 값
- int w: 동영상 또는 웹캠 프레임의 너비
- int h: 동영상 또는 웹캠 프레임의 높이
- int frames: Object detection 수행할 프레임 수 (동영상에서 사용)
- int fullscreen: Object detection 결과를 풀스크린으로 표시할지 여부
동작:
- YOLO 모델을 로드하고, 웹캠 또는 동영상을 캡처하기 위한 초기화 작업을 수행한다.
- Object detection 결과를 저장하기 위한 배열과 변수를 초기화한다.
- Object detection을 위한 fetch, detect, display 스레드를 생성하고, 결과를 출력한다.
- prefix가 설정되어 있으면 Object detection 결과를 이미지 파일로 저장한다.
- demo_done 플래그가 설정되면 프로그램을 종료한다.
설명:
- 이 코드는 YOLO 알고리즘을 사용하여 object detection을 수행하는 데모 프로그램이다.
- 프로그램은 웹캠 또는 동영상을 입력으로 받아서 Object detection을 수행하고, 결과를 실시간으로 출력한다.
- 프로그램은 fetch, detect, display 스레드를 생성하여 Object detection 처리를 병렬화하고, 최적화된 성능을 보인다.
- 프로그램에서 사용하는 fetch_in_thread, detect_in_thread, display_in_thread 함수들은 각각 fetch, detect, display 스레드에서 실행되는 함수이다.
- demo 함수는 이들 스레드를 생성하고, Object detection 결과를 출력하는 메인 루프 역할을 수행한다.
demo error
1
2
3
4
5
6
#else
void demo(char *cfgfile, char *weightfile, float thresh, int cam_index, const char *filename, char **names, int classes, int delay, char *prefix, int avg, float hier, int w, int h, int frames, int fullscreen)
{
fprintf(stderr, "Demo needs OpenCV for webcam images.\n");
}
#endif
함수 이름: demo
입력:
- char *cfgfile: YOLO 모델의 구성 파일 경로
- char *weightfile: 학습된 YOLO 모델의 가중치 파일 경로
- float thresh: 객체 탐지 임계값
- int cam_index: 사용할 카메라의 인덱스
- const char *filename: 사용할 비디오 파일 경로
- char **names: 클래스 이름 배열
- int classes: 클래스 수
- int delay: 비디오 재생 프레임 간 딜레이 (밀리초 단위)
- char *prefix: 결과 이미지 파일 이름의 prefix
- int avg: YOLO 모델에서 사용되는 평균화 프레임 수
- float hier: YOLO 모델에서 사용되는 Hierarchy 임계값
- int w: 입력 이미지의 너비
- int h: 입력 이미지의 높이
- int frames: 비디오에서 읽을 프레임 수
- int fullscreen: 전체 화면 모드 여부
동작:
- YOLO 모델을 로드하고 입력 이미지를 처리하며 객체 탐지 결과를 표시하는 데모를 수행한다.
- OpenCV를 사용하여 웹캠 또는 비디오 파일에서 입력 이미지를 가져온다.
- YOLO 모델에서 객체 탐지를 위해 fetch_in_thread 및 detect_in_thread 함수를 실행하는 스레드를 생성한다.
- display_in_thread 함수를 사용하여 객체 탐지 결과를 화면에 표시한다.
- 비디오에서 프레임을 읽고 이미지를 처리한 후 결과 이미지를 파일로 저장할 수 있다.
- 프로그램 종료 조건인 demo_done이 true가 될 때까지 무한 루프를 실행한다.
설명:
- 이 함수는 OpenCV를 사용하여 웹캠 또는 비디오 파일에서 입력 이미지를 가져와 YOLO 모델을 사용하여 객체를 탐지하는 데모를 수행한다.
- 만약 OpenCV가 설치되어 있지 않은 경우에는 “Demo needs OpenCV for webcam images.” 메시지가 출력된다.
- 이 함수에서는 fetch_in_thread 및 detect_in_thread 함수를 실행하는 스레드를 생성하여 객체 탐지 속도를 향상시키고, display_in_thread 함수를 사용하여 객체 탐지 결과를 화면에 표시한다.
- 또한, 비디오에서 프레임을 읽고 이미지를 처리한 후 결과 이미지를 파일로 저장할 수 있다.
This post is licensed under CC BY 4.0 by the author.