Skip to content

Customizing Solutions

Customizing a Solution can be accomplished by editing its Lua code. All CVEDIA-RT solutions are broken down in different folders and .json files that are reflected in the instance list sidebar:



If we want to change the drone-detection -> detector-thermal solution, we will be able to find it at assets/projects/drone-detection/detector-thermal.json , where all lua scripts pertaining to the drone-detection project will live under assets/projects/drone-detection/lua.

CVEDIA-RT on Dockers

When running CVEDIA-RT within docker (Linux deployments) solutions are embedded within the docker image. You can run to expose all included projects. This script will copy all projects from the docker image to the projects folder, which is automatically mounted when you start CVEDIA-RT using


Solution Configuration

A solution configuration is set by the base_config.json file present on the solution root folder. This file sets the common properties for all the instances that are children of that solution. Each remaining JSON file define an instance configuration. When an instance starts, it will inherit the solution configuration from the base_config.json file and will add or replace its own settings.

Accessing Solution Configuration

As an example, we show how to define a my_var variable in the JSON and retrieving it from the code.

base_config.json or instance.json:

"Global": {
    "my_var": "my var content"

And inside the lua code we can retrieve it by using:

local lua_var = instance:getConfigValue("Global/my_var")

Solution Lua Code

Every solution has a solution.lua file tha must be present on the project folder. This file is responsible for creating the solution scope interfaces and modules. It also defines the options to expose in the Input Configuration panel.

A base template for this file is the following:

dofile(luaroot .. "/api/api.lua")

[email protected] solution Solution
function onStartup(solution)
    print("Starting solution")
    local detectInst = api.factory.inference.create(solution, "PVADetector")
    solution:registerUiCallback("onInputSinkConfig", "onInputSinkConfig", inputSinkConfig)

[email protected] solution Solution
[email protected] instance rt_instance
function onStartInstance(solution, instance)
    print("Running instance " .. instance:getName())

[email protected] solution Solution
[email protected] instance rt_instance
function onStopInstance(solution, instance)
    print("Stopping instance " ..  instance:getName())

[email protected] solution Solution
function onShutdown(solution)
    print("Shutting down solution")
    api.factory.inference.delete(solution, "PVADetector")
    solution:unregisterUiCallback("onInputSinkConfig", "onInputSinkConfig")

function inputSinkConfig(input)
    local config = {
        {name = "Detector regions", configkey = "DetectorRegions", introtext = "Create custom detection regions to replace the full frame detection", itemprefix = "Region", type = "rectangle"}

    return config

Instance script

An instance main code file is called index.lua

It can be changed on the JSON file of each instance, or in the base_config.json by setting the lua_script property of the Global object:

    "Global": {
        "lua_script": "lua/custom_index.lua"

index.lua Structure

File imports

The first thing index.lua does is importing other lua files it may need.

dofile(luaroot .. "/api/api.lua")
dofile(project_root .. "classifier_tracker.lua")
dofile(project_root .. "utils.lua")

Lua state global vars

Lua state always has the variables luaroot and project_root defined for the main lua API folder inside CVEDIA-RT and current lua project folder path.


Next the onStartInstance function is defined, this function is the first being called when starting the instance and should be where all the workers are defined.


In CVEDIA-RT a Worker is an instance thread that has its own onInit and onRun functions. This methods allows multithreading for the instances.

function onStartInstance(name, config)
    local instance = api.thread.getCurrentInstance()
    instance:createWorker("onInit", "onRun")


The onInit() function runs once when starting the Instance.

Initializing modules

Usually a module can be loaded by calling api.factory.[MODULENAME].create(instance, [MODULENAME_ON_JSON]).

[MODULENAME_ON_JSON] is used to read and write the settings of the module in the instance or solution config JSON.

function onInit(instance)

    local inputInst = api.factory.input.create(instance, "Input")

    local outputInst = api.factory.output.create(instance, "Output")

    zoneInst =, "Zone")

    -- Create other modules (...)
    return true

onInit() return value

onInit() must return true, signaling the engine that initialization was completed successfully.


The onRun() function runs continuosly and it's where the video analitycs logic is implemented

function OnRun(currentTime)

    local instance = api.thread.getCurrentInstance()

    -- Get next frame. If the instance is paused we must ignore the skip_frame functionality
    local inputFrames = input:readMetaFrames(instance:isPaused())

    if #inputFrames == 0 then
        -- No frames available yet due to FPS syncronization
        return InstanceStepResult.INSTANCE_STEP_INVALID

    local inputimg = inputFrames[1].image
    local metadata = inputFrames[1].metadata -- data received from the REST API or other protocols

    if inputimg == nil or not inputimg:is_valid() then
        return InstanceStepResult.INSTANCE_STEP_INVALID

    -- Run detector
    -- DetectorRegions is the name given in the inputSinkConfig function for storing the detection region
    -- settings, as defined on the solution.lua code
    [email protected] Rect[]
    local regions = instance:getConfigValue("DetectorRegions")

    local detectInst = api.factory.inference.get(instance:getSolution(), "PVADetector")

    [email protected] Detection[]
    local detections = detectInst:runInference(regions, inputimg)

    Instance:writeOutputSink("source", OutputType.Image, inputimg, "Input image")
    Instance:writeOutputSink("detections", OutputType.BBox, detections, "Raw detections (untracked)")

    return InstanceStepResult.INSTANCE_STEP_OK

Ouptut Sinks

For more information on the Output Sink check here.

onRun() currentTime parameter

The onRun() function always receives a parameter that indicates the real timestamp at which the function was called. This can be used for timing and performance data gathering.