How to get classifier information in deepstream?

I have a primary detector and a secondary classifier working on top of the detected objects. So far in the app, how would I be able to obtain secondary classifier results from the deepstream app?

Read code:
sources/libs/nvdsinfer_customparser/nvdsinfer_customclassifierparser.cpp

@ChrisDing, So the inbuilt parsing function which is used by the sample deepstream app isn’t populating the classifier information into the structures I believe? Am I right or my understanding is wrong?

@ChrisDing, could you also let me know which structure the output labels are getting populated to?

https://docs.nvidia.com/metropolis/deepstream/4.0/dev-guide/DeepStream_Development_Guide/baggage/struct__NvDsObjectMeta.html#aafb2c2d333770f4c997455659aa3dad4

Is that the list holding classifier output? All it tells me is that it is only a list of pointers. Any idea as to what the content are to which it points to?

I also looked at the part of customclassifier parser, but I do not know where the classifier output is getting stored in the structures offered in deepstream. Any idea? Right now I’ve modified the deepstream-test2 app to run on single primary detector and a secondary classifier. Trying to fetch that output has been the issue, i.e car color or make any output from classifier

FAQ: https://devtalk.nvidia.com/default/topic/1061492/deepstream-sdk/deepstream-sdk-faq/post/5375261/#5375261

-> 1. Source code diagram Step6, 7
This can help you get the answer. You can get metadata (primary detector, secondary classifier result) from osd_sink_pad_buffer_probe() in deepstream-test2

@ChrisDing, thanks for the link, will check it out.
So, by default Deepstream only populates info from primary detector, not from the classifiers, i.e,

obj_meta = (NvDsObjectMeta *) (l_obj->data);
obj_meta->class_id == PGIE_CLASS_ID_VEHICLE

This is something which we did for object detector output. So similar thing is not being populated, so the user has to do it himself right. Just to be sure. Thanks.

Metadata includes both primary detector and classifier results.

static GstPadProbeReturn
osd_sink_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
    gpointer u_data)
{
    GstBuffer *buf = (GstBuffer *) info->data;

    NvDsLabelInfo *classifier_meta = NULL;

    guint num_rects = 0;
    NvDsObjectMeta *obj_meta = NULL;
    guint vehicle_count = 0;
    guint person_count = 0;
    NvDsMetaList * l_frame = NULL;
    NvDsMetaList * l_obj = NULL;
    NvDsDisplayMeta *display_meta = NULL;

    NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);

    for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
      l_frame = l_frame->next) {
        NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) (l_frame->data);

        int offset = 0;
        for (l_obj = frame_meta->obj_meta_list; l_obj != NULL;
                l_obj = l_obj->next) {
            obj_meta = (NvDsObjectMeta *) (l_obj->data);
            classifier_meta = (NvDsLabelInfo *) (l_obj->data);

            int val_id = classifier_meta->result_class_id;
            g_print ("%d\n", val_id);

            if (obj_meta->class_id == PGIE_CLASS_ID_VEHICLE) {
                vehicle_count++;
                num_rects++;
            }
            if (obj_meta->class_id == PGIE_CLASS_ID_PERSON) {
                person_count++;
                num_rects++;
                obj_meta->text_params.font_params.font_size = 24;
            }
        }
        display_meta = nvds_acquire_display_meta_from_pool(batch_meta);
        NvOSD_TextParams *txt_params  = &display_meta->text_params[0];
        display_meta->num_labels = 1;
        txt_params->display_text = g_malloc0 (MAX_DISPLAY_LEN);
        offset = snprintf(txt_params->display_text, MAX_DISPLAY_LEN, "Person = %d ", person_count);
        offset = snprintf(txt_params->display_text + offset , MAX_DISPLAY_LEN, "Vehicle = %d ", vehicle_count);

        /* Now set the offsets where the string should appear */
        txt_params->x_offset = 10;
        txt_params->y_offset = 12;

        /* Font , font-color and font-size */
        txt_params->font_params.font_name = "Serif";
        txt_params->font_params.font_size = 24;
        txt_params->font_params.font_color.red = 1.0;
        txt_params->font_params.font_color.green = 1.0;
        txt_params->font_params.font_color.blue = 1.0;
        txt_params->font_params.font_color.alpha = 1.0;

        /* Text background color */
        txt_params->set_bg_clr = 1;
        txt_params->text_bg_clr.red = 0.0;
        txt_params->text_bg_clr.green = 0.0;
        txt_params->text_bg_clr.blue = 0.0;
        txt_params->text_bg_clr.alpha = 1.0;

        nvds_add_display_meta_to_frame(frame_meta, display_meta);
    }

    // g_print ("Frame Number = %d Number of objects = %d "
    //         "Vehicle Count = %d Person Count = %d\n",
    //         frame_number, num_rects, vehicle_count, person_count);
    frame_number++;
    return GST_PAD_PROBE_OK;
}

According to the above, I did try and get classifier results from the metadata but I get junk data. Any idea what I could be doing wrong?

I’m trying to print the classifier output from line 27 to line 30.

@ChrisDing, could you let me know if I’m doing anything wrong with the above code?

Can you refer to test2 sample osd_sink_pad_buffer_probe() ?

@ChrisDing, this is the function from the test2 app, I did refer to it,

static GstPadProbeReturn
osd_sink_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
    gpointer u_data)
{
    GstBuffer *buf = (GstBuffer *) info->data;
    guint num_rects = 0;
    NvDsObjectMeta *obj_meta = NULL;
    guint vehicle_count = 0;
    guint person_count = 0;
    NvDsMetaList * l_frame = NULL;
    NvDsMetaList * l_obj = NULL;
    NvDsDisplayMeta *display_meta = NULL;

    NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);

    for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
      l_frame = l_frame->next) {
        NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) (l_frame->data);
        int offset = 0;
        for (l_obj = frame_meta->obj_meta_list; l_obj != NULL;
                l_obj = l_obj->next) {
            obj_meta = (NvDsObjectMeta *) (l_obj->data);
            if (obj_meta->class_id == PGIE_CLASS_ID_VEHICLE) {
                vehicle_count++;
                num_rects++;
            }
            if (obj_meta->class_id == PGIE_CLASS_ID_PERSON) {
                person_count++;
                num_rects++;
            }
        }
        display_meta = nvds_acquire_display_meta_from_pool(batch_meta);
        NvOSD_TextParams *txt_params  = &display_meta->text_params[0];
        display_meta->num_labels = 1;
        txt_params->display_text = g_malloc0 (MAX_DISPLAY_LEN);
        offset = snprintf(txt_params->display_text, MAX_DISPLAY_LEN, "Person = %d ", person_count);
        offset = snprintf(txt_params->display_text + offset , MAX_DISPLAY_LEN, "Vehicle = %d ", vehicle_count);

        /* Now set the offsets where the string should appear */
        txt_params->x_offset = 10;
        txt_params->y_offset = 12;

        /* Font , font-color and font-size */
        txt_params->font_params.font_name = "Serif";
        txt_params->font_params.font_size = 10;
        txt_params->font_params.font_color.red = 1.0;
        txt_params->font_params.font_color.green = 1.0;
        txt_params->font_params.font_color.blue = 1.0;
        txt_params->font_params.font_color.alpha = 1.0;

        /* Text background color */
        txt_params->set_bg_clr = 1;
        txt_params->text_bg_clr.red = 0.0;
        txt_params->text_bg_clr.green = 0.0;
        txt_params->text_bg_clr.blue = 0.0;
        txt_params->text_bg_clr.alpha = 1.0;

        nvds_add_display_meta_to_frame(frame_meta, display_meta);
    }

    g_print ("Frame Number = %d Number of objects = %d "
            "Vehicle Count = %d Person Count = %d\n",
            frame_number, num_rects, vehicle_count, person_count);
    frame_number++;
    return GST_PAD_PROBE_OK;
}

But nowhere in the code or any other sample apps are they printing out classifier info on the terminal for the object. Atleast that’s what I think of.

Edit: I don’t know if I made the question clear, but basically what I’m trying to achieve is, printing number of white cars or number of sedans or number of red color cars. Nowhere in any of the apps this information is being accessed in the probe. I wanted to know how.

Can you check gstnvinfer_meta_utils.cpp -> attach_metadata_classifier() -> line 224

label_info->label_id = attr.attributeIndex;
label_info->result_class_id = attr.attributeValue;
label_info->result_prob = attr.attributeConfidence;

nvdsmeta.h
NvDsFrameMeta -> obj_meta_list (NvDsObjectMeta) -> classifier_meta_list (NvDsClassifierMeta) -> label_info_list (NvDsLabelInfo)

Metadata doc:
https://docs.nvidia.com/metropolis/deepstream/dev-guide/DeepStream_Development_Guide/baggage/group__metadata__structures.html

@ChrisDing, yes I was using the metadata structures to access the classifier input, since

obj_meta contains classifier_meta_list and it internally contains label_info_list. Should parse that using it.

I don’t think

gstnvinfer_meta_utils.cpp -> attach_metadata_classifier() -> line 224

is the right way to access it, since that code is used to modify the content of those structures

Here you go, this is probably the way to access it I guess.

static GstPadProbeReturn
osd_sink_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
    gpointer u_data)
{
    GstBuffer *buf = (GstBuffer *) info->data;

    // NvDsLabelInfo *classifier_meta = NULL;

    guint num_rects = 0;
    NvDsObjectMeta *obj_meta = NULL;
    guint vehicle_count = 0;
    guint person_count = 0;
    NvDsMetaList * l_frame = NULL;
    NvDsMetaList * l_obj = NULL;
    NvDsDisplayMeta *display_meta = NULL;
    NvDsClassifierMetaList *l_classifier = NULL;
    NvDsClassifierMeta *class_meta = NULL;
    NvDsLabelInfoList *l_label = NULL;
    NvDsLabelInfo *label_info = NULL;
    NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);

    for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
      l_frame = l_frame->next) {
        NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) (l_frame->data);

        int offset = 0;
        for (l_obj = frame_meta->obj_meta_list; l_obj != NULL;
                l_obj = l_obj->next) {
            obj_meta = (NvDsObjectMeta *) (l_obj->data);

            // g_print ("%s\n", classifier_meta->result_label);
            // g_print("%f\n", prob);

            if (obj_meta->class_id == PGIE_CLASS_ID_VEHICLE) {
                vehicle_count++;
                num_rects++;

                int id = obj_meta->object_id;
                for(l_classifier = obj_meta->classifier_meta_list; l_classifier != NULL;
                    l_classifier = l_classifier->next) {
                      class_meta = (NvDsClassifierMeta *) (l_classifier->data);
                      for(l_label = class_meta->label_info_list; l_label != NULL;
                      l_label = l_label->next) {
                        label_info = (NvDsLabelInfo *) (l_label->data);
                        g_print("%d", id);
                        g_print("-------");
                        g_print ("%s\n", label_info->result_label);
                      }
                    }

            }
            if (obj_meta->class_id == PGIE_CLASS_ID_PERSON) {
                person_count++;
                num_rects++;
                obj_meta->text_params.font_params.font_size = 24;
            }

            // NvDsLabelInfo *label_info =
            //     nvds_acquire_label_info_meta_from_pool (batch_meta);

            // g_print ("%s\n", label_info->result_label);
        }
        display_meta = nvds_acquire_display_meta_from_pool(batch_meta);
        NvOSD_TextParams *txt_params  = &display_meta->text_params[0];
        display_meta->num_labels = 1;
        txt_params->display_text = g_malloc0 (MAX_DISPLAY_LEN);
        offset = snprintf(txt_params->display_text, MAX_DISPLAY_LEN, "Person = %d ", person_count);
        offset = snprintf(txt_params->display_text + offset , MAX_DISPLAY_LEN, "Vehicle = %d ", vehicle_count);

        /* Now set the offsets where the string should appear */
        txt_params->x_offset = 10;
        txt_params->y_offset = 12;

        /* Font , font-color and font-size */
        txt_params->font_params.font_name = "Serif";
        txt_params->font_params.font_size = 24;
        txt_params->font_params.font_color.red = 1.0;
        txt_params->font_params.font_color.green = 1.0;
        txt_params->font_params.font_color.blue = 1.0;
        txt_params->font_params.font_color.alpha = 1.0;

        /* Text background color */
        txt_params->set_bg_clr = 1;
        txt_params->text_bg_clr.red = 0.0;
        txt_params->text_bg_clr.green = 0.0;
        txt_params->text_bg_clr.blue = 0.0;
        txt_params->text_bg_clr.alpha = 1.0;

        nvds_add_display_meta_to_frame(frame_meta, display_meta);
    }

    // g_print ("Frame Number = %d Number of objects = %d "
    //         "Vehicle Count = %d Person Count = %d\n",
    //         frame_number, num_rects, vehicle_count, person_count);
    frame_number++;
    return GST_PAD_PROBE_OK;
}

Apparently I’m only using a single classifier. So this fixes the task which I was trying to achieve. Thanks @ChrisDing.

Good.

I want recieve classifier_meta_list in test3. I recieve always NULL. Why?
my code:

static GstPadProbeReturn
tiler_src_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
    gpointer u_data)
{
    GstBuffer *buf = (GstBuffer *) info->data;
    guint num_rects = 0; 
    NvDsObjectMeta *obj_meta = NULL;
    guint vehicle_count = 0;
    guint person_count = 0;
    NvDsMetaList * l_frame = NULL;
    NvDsMetaList * l_obj = NULL;
    //NvDsDisplayMeta *display_meta = NULL;

    NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);

    for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
      l_frame = l_frame->next) {
        NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) (l_frame->data);
        //int offset = 0;

        for (l_obj = frame_meta->obj_meta_list; l_obj != NULL;
                l_obj = l_obj->next) {
            obj_meta = (NvDsObjectMeta *) (l_obj->data);
            for (NvDsMetaList * l_class = obj_meta->classifier_meta_list; l_class != NULL;
                l_class = l_class->next) {
              NvDsClassifierMeta *cmeta = (NvDsClassifierMeta *) l_class->data;
              for (NvDsMetaList * l_label = cmeta->label_info_list; l_label != NULL;
                  l_label = l_label->next) {
                NvDsLabelInfo *label = (NvDsLabelInfo *) l_label->data;
                g_print("class:%s", label->result_label);
                }
              }   
            if (obj_meta->class_id == PGIE_CLASS_ID_VEHICLE) {
                vehicle_count++;
                num_rects++;
            }
            if (obj_meta->class_id == PGIE_CLASS_ID_PERSON) {
                person_count++;
                num_rects++;
            }
        }
          g_print ("Frame Number = %d Number of objects = %d "
            "Vehicle Count = %d Person Count = %d\n",
            frame_meta->frame_num, num_rects, vehicle_count, person_count);
    }
    return GST_PAD_PROBE_OK;
}

Hi chernenko.vasiliy,

Please help to open a new topic for your issue. Thanks

Hello.
I got all needed info from classifier, from obj_meta->text_params.display_text. But how do i get it separatedly?

Hello.
So i just reverse parsed result string into all needed by SGIE number by for(ii=0;;ii++) if (strstr(display_text, sgieN_class_str[ii]);

Best regards.