AlexeyAB / darknet

YOLOv4 / Scaled-YOLOv4 / YOLO - Neural Networks for Object Detection (Windows and Linux version of Darknet )
http://pjreddie.com/darknet/
Other
21.57k stars 7.95k forks source link

How to make the changes in `darknet_images.py` work? #8615

Open LeongVan opened 1 year ago

LeongVan commented 1 year ago

I want to get the original coordinates of detected objects. Firstly, I used the command below.

./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights data/images/test4.png -save_labels -dont_show -ext_output

And I got a .txt file in the same directory with source image in data/images/test4.png named data/images/test4.txt, and it looks like below. 728-2 But I want original coordinates of the objects (the pixel coordinates, before scaled), so I change the source code of writing function called save_annotations and convert2relative in darknet_images.py, showing as below. 728-3 Then, I saved the code file and ran make clean and make -j commands in terminal. After these, I delete data/images/test4.txt and ran the command below again.

./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights data/images/test4.png -save_labels -dont_show -ext_output

But I got the same result as I changed the code before. How can I make my changes work so that I can get original coordinates of the detected objects?

Appreciate any attempts and answers!

LeongVan commented 1 year ago

When I changed image_opencv.cpp and image.c in /darknet/src, and then ran make clean, make -j, I can observed the changes I make, but this do NOT works for darknet_images.py.

bariscankurtkaya commented 1 year ago

Hello @LeongVan ! I'm not sure. So, we will solve this problem with collaboration I guess 😄. I think you misunderstand the way Makefile works. If I'm not wrong Makefile builds the c and cpp files instead of python files. So when you change something in python and build it again the ./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights data/images/test4.png -save_labels -dont_show -ext_output output will not change because ./darknet is build of c and cpp files. If you want to use the command you should change the c files instead of python but you can write a python script to use the darknet_images.py file too.

So, If you want to apply the changes to the python file you need to use a python script that calls darknet_images.py but If you want to use ./darknet command you should change the c file. I hope I could explain.

LeongVan commented 1 year ago

Hello @LeongVan ! I'm not sure. So, we will solve this problem with collaboration I guess 😄. I think you misunderstand the way Makefile works. If I'm not wrong Makefile builds the c and cpp files instead of python files. So when you change something in python and build it again the ./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights data/images/test4.png -save_labels -dont_show -ext_output output will not change because ./darknet is build of c and cpp files. If you want to use the command you should change the c files instead of python but you can write a python script to use the darknet_images.py file too.

So, If you want to apply the changes to the python file you need to use a python script that calls darknet_images.py but If you want to use ./darknet command you should change the c file. I hope I could explain.

Thanks! I think you're right with how Makefile works, and I already have change the C and C++ files so that I can get what I want. And it worked. But here's the problem. If I run ./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights data/images/test.png -dont_show -save_labels, there is an output file called test.txt saving all the detected information, showing as below. image I assume the codes that write down these information are in darknet_images.py, which means that this file are called (maybe). But any changes in this file do not works. If this file is not called, how is this test.txt saved? If this file is called, why any changes to it do not work? Maybe we can figure it out together.

bariscankurtkaya commented 1 year ago

Yes, I finally solved the problem @LeongVan !

You can use the #8622 branch with

./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights data/dog.jpg -save_labels -dont_show -ext_output

If you are curious about my adventure you can read below 🥳

I dig it up a little bit and I found a code block that checks the -save_labels flag. You can find the code in ./src/detector.c - line 1732.

if (save_labels)
        {
            char labelpath[4096];
            replace_image_to_label(input, labelpath);

            FILE* fw = fopen(labelpath, "wb");
            int i;
            for (i = 0; i < nboxes; ++i) {
                char buff[1024];
                int class_id = -1;
                float prob = 0;
                for (j = 0; j < l.classes; ++j) {
                    if (dets[i].prob[j] > thresh && dets[i].prob[j] > prob) {
                        prob = dets[i].prob[j];
                        class_id = j;
                    }
                }
                if (class_id >= 0) {
                    sprintf(buff, "%d %2.4f %2.4f %2.4f %2.4f\n", class_id, dets[i].bbox.x, dets[i].bbox.y, dets[i].bbox.w, dets[i].bbox.h);
                    fwrite(buff, sizeof(char), strlen(buff), fw);
                }
            }
            fclose(fw);
        }

after that I looked the code and try to find where we assign the dets array. Then I found out there is a function which is creating bounding boxes. get_network_boxes You can find the function in ./src/network.c line 964.

detection *get_network_boxes(network *net, int w, int h, float thresh, float hier, int *map, int relative, int *num, int letter)
{
    detection *dets = make_network_boxes(net, thresh, num);
    fill_network_boxes(net, w, h, thresh, hier, map, relative, dets, letter);
    return dets;
}

Then as you can see It calls another function 🤣 and FINALLY we can find the main code in ./src/network.c line 814

detection *make_network_boxes(network *net, float thresh, int *num)
{
    int i;
    layer l = net->layers[net->n - 1];
    for (i = 0; i < net->n; ++i) {
        layer l_tmp = net->layers[i];
        if (l_tmp.type == YOLO || l_tmp.type == GAUSSIAN_YOLO || l_tmp.type == DETECTION || l_tmp.type == REGION) {
            l = l_tmp;
            break;
        }
    }

    int nboxes = num_detections(net, thresh);
    if (num) *num = nboxes;
    detection* dets = (detection*)xcalloc(nboxes, sizeof(detection));
    for (i = 0; i < nboxes; ++i) {
        dets[i].prob = (float*)xcalloc(l.classes, sizeof(float));
        // tx,ty,tw,th uncertainty
        if(l.type == GAUSSIAN_YOLO) dets[i].uc = (float*)xcalloc(4, sizeof(float)); // Gaussian_YOLOv3
        else dets[i].uc = NULL;

        if (l.coords > 4) dets[i].mask = (float*)xcalloc(l.coords - 4, sizeof(float));
        else dets[i].mask = NULL;

        if(l.embedding_output) dets[i].embeddings = (float*)xcalloc(l.embedding_size, sizeof(float));
        else dets[i].embeddings = NULL;
        dets[i].embedding_size = l.embedding_size;
    }
    return dets;
}

Then I realize this isn't what they want LoL 😭 So I look at the draw_detections_v3 function which is inside of the ./src/image.c line 329 and I found out a new function which is:

detection_with_class* selected_detections = get_actual_detections(dets, num, thresh, &selected_detections_num, names);

Then I refactored the whole save_labels code block and on the other hand, I added -save_labels_actual for your problem.

You can find the refactored code blocks from ./src/utils.c line 224 in #8622 branch.

LeongVan commented 1 year ago

BRILLIANT!!! I'll try it out later. THANKS!!!