Using Nvds Analytics to get per class count with the line counter

My system has the following specs:
GPU: GTX 1660ti
RAM: 32 GB
OS: Windows 11
Environment: Ubuntu 24.04.1 LTS (Through WSL)
Deepstream Version: 7.1
Nvidia-Driver Version: 566.03 | CUDA Version: 12.7

I’ve modified the test5 application by using code from the deepstream-occupancy-analytics .

I had a previous post here where I figured out how to extract and grab values the nvds analytics module and send them over to kafka here:

Now I have the issue of not being able to extract per class count using the line counter of NVDS analytics.

Using NvDsAnalyticsFrameMeta, I have already tried to extracted the count with objCnt but objCnt only provides the total number of objects per class per frame

I need to know the count of each class when using [line-crossing-stream-0] in the nvds-analytics config file so that I can get the count of objects per class that cross the lines and not the overall accumulated count.

Your Help is much appreciated.

what model are you using? what model class will cross the line? do you want to use objLCCurrCnt or objLCCumCnt?

what model are you using?
I am using a custom YOLO model with 10 different classes.

what model class will cross the line?
An object crossing the line can belong to any one of those 10 classes.

Do you want to use objLCCurrCnt or objLCCumCnt?
I am already extracted values from objLCCurrCnt and objLCCumCnt. They only provide an accumulated count of all class IDs mentioned at class-id in the nvds analytics config but doesn’t give me the per class breakdown.

I need to extract the per class count of all objects crossing the line.

please refer to parse_nvdsanalytics_meta_data in \opt\nvidia\deepstream\deepstream\sources\apps\sample_apps\deepstream-nvdsanalytics-test\deepstream_nvdsanalytics_meta.cpp, every object has a NVDS_USER_OBJ_META_NVDSANALYTICS type user meta, which saves NvDsAnalyticsObjInfo. you can use lcStatus of NvDsAnalyticsObjInfo to check if the object crosses the line.

Here is my code:

extern "C" void
analytics_custom_parse_nvdsanalytics_meta_data (NvDsMetaList *l_user, AnalyticsUserMeta *data)
{
    NvDsUserMeta *user_meta = (NvDsUserMeta *) l_user->data;

    if (!user_meta) {
      g_print("User meta is nullptr\n");
      return;
    }

    /* First, check if this is frame-level analytics meta. */
    if (user_meta->base_meta.meta_type == NVDS_USER_FRAME_META_NVDSANALYTICS) {
        NvDsAnalyticsFrameMeta *meta =
            (NvDsAnalyticsFrameMeta *) user_meta->user_meta_data;

        /* --- Existing code that reads from frame-level meta --- */
        data->exit_count       = 0;
        data->total_count      = 0;
        data->entry_count      = 0;
        data->source_id        = 0;
        data->per_class_count  = NULL;
        data->all_count_values = NULL;

        data->exit_count  = meta->objLCCumCnt["Exit"];
        data->entry_count = meta->objLCCumCnt["Entry"];

        if (meta->objLCCumCnt["Entry"] > meta->objLCCumCnt["Exit"]) {
            data->total_count = meta->objLCCumCnt["Entry"] - meta->objLCCumCnt["Exit"];
        } else if (meta->objLCCumCnt["Exit"] > meta->objLCCumCnt["Entry"]) {
            data->total_count = meta->objLCCumCnt["Exit"] - meta->objLCCumCnt["Entry"];
        }

        std::stringstream all_count_values_ss;
        all_count_values_ss << "|" 
                            << data->entry_count << "|"
                            << data->exit_count  << "|"
                            << data->total_count << "|";

        for (const auto &class_count : meta->objCnt) {
            all_count_values_ss << class_count.first << ":"
                                << class_count.second << ";";
        }

        std::string all_count_values_str = all_count_values_ss.str();
        data->all_count_values = g_strdup(all_count_values_str.c_str());
        /* --- End of existing frame-level code --- */
    }

    /* Otherwise, check if this is object-level analytics meta. */
    else if (user_meta->base_meta.meta_type == NVDS_USER_OBJ_META_NVDSANALYTICS) {
        NvDsAnalyticsObjInfo *obj_info =
            (NvDsAnalyticsObjInfo *) user_meta->user_meta_data;
        if (!obj_info) {
          g_print("Failed to cast user_meta_data to NvDsAnalyticsObjInfo\n");
          return;
        }

        // Access and print lcStatus
        std::stringstream lane_string;
        for (const auto &lane : obj_info->lcStatus) {
            lane_string << lane << " ";
        }
        g_print("LineLabel: %s\n", lane_string.str().c_str());

        // Print direction status if available
        if (!obj_info->dirStatus.empty()) {
            g_print("Object moving in %s\n", obj_info->dirStatus.c_str());
        }
    }

    else {
      g_print("User meta is neither frame-level nor object-level analytics meta.\n");
    }
}

I keep on getting the following output:
LineLabel: Exit
LineLabel: Exit
LineLabel: Exit
LineLabel: Exit

It seems in my case that lcStatus only contains the value ‘Exit’ which is just the name of the line.

I am calling the function analytics_custom_parse_nvdsanalytics_meta_data(l_obj_user, user_data_obj); at object level like this:

    for (GList *l = frame_meta->obj_meta_list; l != NULL; l = l->next) {
      NvDsObjectMeta *obj_meta = (NvDsObjectMeta *) (l->data);

      // Loop through the object-level user meta
      for (NvDsMetaList *l_obj_user = obj_meta->obj_user_meta_list;
           l_obj_user != NULL; l_obj_user = l_obj_user->next)
      {
        // Optionally, you can allocate a new AnalyticsUserMeta, or
        // reuse one if you wish— up to you.
        AnalyticsUserMeta *user_data_obj =
            (AnalyticsUserMeta *) g_malloc0(sizeof(AnalyticsUserMeta));
        user_data_obj->source_id = stream_id;

        analytics_custom_parse_nvdsanalytics_meta_data(l_obj_user, user_data_obj);

Still, I am not sure what the problem is

@fanzh
Any update on this?

@fanzh still waiting on a response from you.

/** Holds the array of line crossing labels which object has crossed */
std::vector std::string lcStatus;
Sorry for the late reply. from the comment, lcStatus means line crossing labels which object has crossed. could you share your expectation?