.. _developing_gstreamer_app_gstreamer_argmaxprint: Step 4: Develop, test and verify output of ``argmax_print`` custom plugin ######################################################################### .. image:: media/resnet50_application_simaaisrc_argmax_print.jpg :align: center :scale: 30% | As a final step, we need to perform ``argmax`` to select the highest probability. The output from the network is an array of 1000 probabilities. Thus, we should expect an output of 1000 fp32 values, or 4000 bytes total (fp32 => 4 bytes). To do so, we will create a custom plugin where we will: #. Take in the buffer from the ``simaaiprocesscvu`` (running the :ref:`ev74_graph_201_sima_detess_dequant` CVU graph) plugin. #. Perform the ``argmax`` operation on it. #. Print the output class index to a console. Creating a custom Plugin ======================== On your host machine's Palette docker install, go to SiMa's pluginzoo directory, copy the ``simple_overlay`` plugin example and compile as a test: .. code-block:: console sima-user@docker-image-id:~$ source /opt/poky/4.0.10/environment-setup-cortexa65-poky-linux sima-user@docker-image-id:~$ cd /usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst$ cp -r examples/opencv/simple_overlay ./argmax_print sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst$ cd argmax_print/ sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print$ mkdir build && cd build sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/build$ cmake .. -- Configuring done -- Generating done -- Build files have been written to: /usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/build sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/build$ make [ 50%] Building CXX object CMakeFiles/argmax_print.dir/payload.cpp.o /usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/payload.cpp: In member function ‘void UserContext::run(std::vector&, std::span)’: /usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/payload.cpp:57:71: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] 57 | cv::imencode(".jpg", image, reinterpret_cast&>(output)); | ^~~~~~ [100%] Linking CXX shared library libargmax_print.so [100%] Built target argmax_print While there are some warnings, it compiles successfully, which means we have copied the plugin successfully and are ready to write our own code into it. First, let's write and compile the plugin, and then we will break down how it works. #. Rename the config to ``argmax_print_cfg.json``: .. code-block:: console sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/build$ cd .. sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print$ ls build CMakeLists.txt overlay.json payload.cpp sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print$ mv overlay.json argmax_print_cfg.json #. Rewrite the contents ``argmax_print_cfg.json`` with: .. code-block:: json :caption: argmax_print_cfg.json :linenos: { "version": 0.1, "node_name": "argmax_print", "memory": { "cpu": 0, "next_cpu": 0 }, "system": { "out_buf_queue": 1, "debug": 0, "dump_data": 0 }, "buffers": { "input": [ { "name": "detess-dequant", "size": 4000 } ], "output": { "size": 4 } } } .. note:: Where the parameters: * ``""node_name"``: Any name you want to give the instance of the plugin * ``["memory"]["cpu"]``: Means we are targeting A65 CPU memory space (``0`` -> A65 CPU, ``1`` -> EV74 CVU, ``2`` or ``4`` -> MLA) * ``["memory"]["next_cpu"]``: Means we are targeting the next plugin downstream to be A65 CPU memory space (``0`` -> A65 CPU, ``1`` -> EV74 CVU, ``2`` or ``4`` -> MLA) * ``["system"]["out_buf_queue"]``: Means how many buffers we are allocating to the output. * ``["system"]["debug"]``: 0 (false) or 1 (true) if we want debugging enabled. * ``["system"]["dump_data"]``: 0 (false) or 1 (true) if we want to dump the output buffer. * ``["buffers"]["input"]["name"]``: The input plugin's name. * ``["buffers"]["input"]["size"]``: The input plugin's buffer size. 1000 probabilities in float32 (4 bytes) is 4000 bytes. * ``["buffers"]["output"]["size"]``: The input plugin's output. We are passing the index of the largest probability to the next plugin. In this case, one int32 value, or 4 bytes. | You can run ``gst-inspect-1.0 --gst-plugin-path=/data/simaai/building_apps_palette/gstreamer/gst-plugins argmax_print`` for more information on the plugin itself. #. Re-write the ``payload.cpp`` with the following: .. code-block:: cpp :caption: payload.cpp :linenos: #include // Custom functions // Function to perform argmax int argmax(const float* buffer, size_t size) { // Use std::max_element to find the index of the largest value return std::distance(buffer, std::max_element(buffer, buffer + size)); } // Here's your init code UserContext::UserContext(nlohmann::json* json) : parser(json) { std::cout << PLUGIN_STR_C " INIT" << std::endl; } // Here's your deinit code UserContext::~UserContext() { std::cout << PLUGIN_STR_C " DEINIT" << std::endl; } // this code will be called on every new frame void UserContext::run(std::vector &input, std::span output) { // Extract the input probabilities float * probabilities = reinterpret_cast(input[0].getData().data()); std::size_t probabilities_size = input[0].getDataSize(); // Perform the argmax operation int max_idx = argmax(probabilities, probabilities_size); // Print the index of the largest value std::cout << "Index of largest value: " << max_idx << std::endl; std::cout << "Largest value: " << probabilities[max_idx] << std::endl; // Send the output out, but first ensure enough memory has been allocated for the output if (output.size() >= sizeof(int)) { // Copy the index into the output buffer (reinterpret int as bytes) std::memcpy(output.data(), &max_idx, sizeof(int)); } else { std::cerr << "Error: Output buffer is too small to store the index." << std::endl; } } #. Make the build directory and compile .. code-block:: console sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print$ source /opt/poky/4.0.10/environment-setup-cortexa65-poky-linux sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print$ cd build sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/build$ cmake .. && make clean && make -- Configuring done -- Generating done -- Build files have been written to: /usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/build [ 50%] Building CXX object CMakeFiles/argmax_print.dir/payload.cpp.o [100%] Linking CXX shared library libargmax_print.so [100%] Built target argmax_print #. From the Palette docker container, copy the ``argmax_print_cfg.json`` and the newly built plugin ``libargmax_print.so`` to the MLSoC board: .. code-block:: console sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/build$ scp libargmax_print.so sima@:/home/sima/gst-plugins/ sima-user@docker-image-id:/usr/local/simaai/plugin_zoo/gst-simaai-plugins-base/gst/argmax_print/build$ scp ../argmax_print_cfg.json sima@:/home/sima/resnet50_example_app/app_configs/ #. Let's update the previous ``run_pipeline.sh`` script to include our new plugin. .. code-block:: bash #!/bin/bash # Constants # APP_DIR=/home/sima/resnet50_example_app DATA_DIR="${APP_DIR}/data" SIMA_PLUGINS_DIR="${APP_DIR}/../gst-plugins" SAMPLE_IMAGE_SRC="${DATA_DIR}/golden_retriever_207_rgb.bin" CONFIGS_DIR="${APP_DIR}/app_configs" PREPROC_CVU_CONFIG_BIN="${CONFIGS_DIR}/genpreproc_200_cvu_cfg_app" PREPROC_CVU_CONFIG_JSON="${CONFIGS_DIR}/genpreproc_200_cvu_cfg_params.json" INFERENCE_MLA_CONFIG_JSON="${CONFIGS_DIR}/simaaiprocessmla_cfg_params.json" DETESSDEQUANT_CVU_CONFIG_BIN="${CONFIGS_DIR}/detessdequant_201_cvu_cfg_app" DETESSDEQUANT_CVU_CONFIG_JSON="${CONFIGS_DIR}/detessdequant_201_cvu_cfg_params.json" ARGMAX_PRINT_CONFIG_JSON="${CONFIGS_DIR}/argmax_print_cfg.json" # Remove any existing temporary files before running rm /tmp/generic_preproc*.out /tmp/mla-*.out /tmp/detess-dequant*.out # Run the configuration apps for generic_preproc and detessdequant $PREPROC_CVU_CONFIG_BIN $PREPROC_CVU_CONFIG_JSON $DETESSDEQUANT_CVU_CONFIG_BIN $DETESSDEQUANT_CVU_CONFIG_JSON # Run the application export LD_LIBRARY_PATH="${SIMA_PLUGINS_DIR}" gst-launch-1.0 -v --gst-plugin-path="${SIMA_PLUGINS_DIR}" \ simaaisrc mem-target=1 node-name="my_image_src" location="${SAMPLE_IMAGE_SRC}" num-buffers=1 ! \ simaaiprocesscvu source-node-name="my_image_src" buffers-list="my_image_src" config="${PREPROC_CVU_CONFIG_JSON}" ! \ simaaiprocessmla config="${INFERENCE_MLA_CONFIG_JSON}" ! \ simaaiprocesscvu source-node-name="mla-resnet" buffers-list="mla-resnet" config="${DETESSDEQUANT_CVU_CONFIG_JSON}" ! \ argmax_print config="${ARGMAX_PRINT_CONFIG_JSON}" ! \ fakesink #. Finally let's run the full end-to-end application! .. code-block:: console davinci:/data/simaai/building_apps_palette/gstreamer/resnet50_example_app$ sudo sh run_pipeline.sh Password: Completed SIMA_GENERIC_PREPROC graph configure Completed Graph Configure ** Message: 00:15:40.779: Num of chunks 1 ** Message: 00:15:40.779: Buffer_name: my_image_src, num_of_chunks:1 (gst-launch-1.0:30032): GLib-GObject-CRITICAL **: 00:15:40.790: g_pointer_type_register_static: assertion 'g_type_from_name (name) == 0' failed (gst-launch-1.0:30032): GLib-GObject-CRITICAL **: 00:15:40.790: g_type_set_qdata: assertion 'node != NULL' failed (gst-launch-1.0:30032): GLib-GObject-CRITICAL **: 00:15:40.791: g_pointer_type_register_static: assertion 'g_type_from_name (name) == 0' failed (gst-launch-1.0:30032): GLib-GObject-CRITICAL **: 00:15:40.791: g_type_set_qdata: assertion 'node != NULL' failed ** Message: 00:15:40.791: Num of chunks 1 ** Message: 00:15:40.792: Buffer_name: mla-resnet, num_of_chunks:1 Setting pipeline to PAUSED ... Argmax_print INIT ** Message: 00:15:40.802: Initialize dispatcher ** Message: 00:15:40.803: handle: 0x83ff95b0, 0xffff83ff95b0 ** Message: 00:15:41.460: Loaded model from location /home/sima/resnet50_example_appmodels/quantized_resnet50_stage1_mla.lm, model:hdl: 0xaaaac0357c60 ** Message: 00:15:41.465: Filename memalloc = /home/sima/resnet50_example_appdata/golden_retriever_207_rgb.bin Pipeline is PREROLLING ... Index of largest value: 207 Largest value: 0.959188 Pipeline is PREROLLED ... Setting pipeline to PLAYING ... Redistribute latency... New clock: GstSystemClock Got EOS from element "pipeline0". Execution ended after 0:00:00.001523003 Setting pipeline to NULL ... Freeing pipeline ... .. note:: If you notice on lines ``27`` and ``28`` we can see the plugins output as we would expect. Conclusion and next steps ========================= In this section, we: * Learned how to develop and build a simple custom plugin using SiMa.ai's custom plugin template (simple_overlay) * Successfully finished our first end-to-end application! Now that we have developed and debugged our end-to-end pipeline, we are are ready to package it into an ML Pipeline Package (``mpk``). This will allow you to conveniently deploy the application on any SiMa.ai MLSoC.