Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions data/ai_models.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
"github_asset": "denoise-nind.dtmodel",
"default": true
},
{
"id": "denoise-nafnet",
"name": "denoise nafnet small",
"description": "NAFNet denoiser trained on SIDD dataset",
"task": "denoise",
"github_asset": "denoise-nafnet.dtmodel",
"default": false
},
{
"id": "upscale-bsrgan",
"name": "upscale bsrgan",
Expand Down
32 changes: 32 additions & 0 deletions src/ai/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,40 @@ typedef struct dt_ai_model_info_t {
const char *arch; ///< e.g. "sam2", "segnext"
const char *backend; ///< Backend type (e.g. "onnx")
int num_inputs; ///< Number of model inputs (default 1)
const char *attributes; ///< Optional attributes
} dt_ai_model_info_t;

/* --- Model "attributes" lookup ---
*
* Models declare optional behavior hints under an "attributes" object
* in their config.json, e.g.:
* "attributes": {
* "shadow_boost": true,
* "tile_factor": 1.5,
* "color_space": "sRGB"
* }
*
* The accessors parse the stored JSON on demand. A missing key (or
* one of a different type) returns the supplied default — or FALSE /
* NULL for the bool / string variants.
*/

gboolean dt_ai_model_attribute_bool(const dt_ai_model_info_t *info,
const char *key);

int dt_ai_model_attribute_int(const dt_ai_model_info_t *info,
const char *key,
int default_value);

double dt_ai_model_attribute_double(const dt_ai_model_info_t *info,
const char *key,
double default_value);

/** Returned string is newly allocated and must be freed with g_free().
* Returns NULL if the key is absent or not a string. */
char *dt_ai_model_attribute_string(const dt_ai_model_info_t *info,
const char *key);

/* --- Discovery --- */

/**
Expand Down
107 changes: 107 additions & 0 deletions src/ai/backend_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,26 @@ static void _scan_directory(dt_ai_environment_t *env, const char *root_path)
? (int)json_object_get_int_member(obj, "num_inputs")
: 1;

// capture optional "attributes" object as a JSON string;
// accessors (e.g. dt_ai_model_attribute_bool) parse on demand
info->attributes = NULL;
if(json_object_has_member(obj, "attributes"))
{
JsonNode *attr_node = json_object_get_member(obj, "attributes");
if(attr_node && JSON_NODE_HOLDS_OBJECT(attr_node))
{
JsonGenerator *gen = json_generator_new();
json_generator_set_root(gen, attr_node);
gchar *s = json_generator_to_data(gen, NULL);
if(s)
{
_store_string(env, s, &info->attributes);
g_free(s);
}
g_object_unref(gen);
}
}

env->models = g_list_prepend(env->models, info);
g_hash_table_insert(
env->model_paths,
Expand Down Expand Up @@ -422,6 +442,93 @@ dt_ai_context_t *dt_ai_load_model_ext(dt_ai_environment_t *env,
return ctx;
}

// model attribute lookup — parses the JSON-encoded attributes string
// on demand; callers pass info from dt_ai_get_model_info_by_id()
//
// _attribute_node returns the parsed JsonParser plus a borrowed JsonNode*
// for the named key; caller must g_object_unref the returned parser;
// returns NULL parser if the attribute set is absent or the key is missing
static JsonParser *_attribute_node(const dt_ai_model_info_t *info,
const char *key,
JsonNode **out_node)
{
*out_node = NULL;
if(!info || !info->attributes || !key) return NULL;
JsonParser *parser = json_parser_new();
if(!json_parser_load_from_data(parser, info->attributes, -1, NULL))
{
g_object_unref(parser);
return NULL;
}
JsonNode *root = json_parser_get_root(parser);
if(!root || !JSON_NODE_HOLDS_OBJECT(root))
{
g_object_unref(parser);
return NULL;
}
JsonObject *obj = json_node_get_object(root);
if(!json_object_has_member(obj, key))
{
g_object_unref(parser);
return NULL;
}
*out_node = json_object_get_member(obj, key);
return parser;
}

gboolean dt_ai_model_attribute_bool(const dt_ai_model_info_t *info,
const char *key)
{
JsonNode *v = NULL;
JsonParser *p = _attribute_node(info, key, &v);
gboolean result = FALSE;
if(v && JSON_NODE_HOLDS_VALUE(v))
result = json_node_get_boolean(v);
if(p) g_object_unref(p);
return result;
}

int dt_ai_model_attribute_int(const dt_ai_model_info_t *info,
const char *key,
int default_value)
{
JsonNode *v = NULL;
JsonParser *p = _attribute_node(info, key, &v);
int result = default_value;
if(v && JSON_NODE_HOLDS_VALUE(v))
result = (int)json_node_get_int(v);
if(p) g_object_unref(p);
return result;
}

double dt_ai_model_attribute_double(const dt_ai_model_info_t *info,
const char *key,
double default_value)
{
JsonNode *v = NULL;
JsonParser *p = _attribute_node(info, key, &v);
double result = default_value;
if(v && JSON_NODE_HOLDS_VALUE(v))
result = json_node_get_double(v);
if(p) g_object_unref(p);
return result;
}

char *dt_ai_model_attribute_string(const dt_ai_model_info_t *info,
const char *key)
{
JsonNode *v = NULL;
JsonParser *p = _attribute_node(info, key, &v);
char *result = NULL;
if(v && JSON_NODE_HOLDS_VALUE(v))
{
const char *s = json_node_get_string(v);
if(s) result = g_strdup(s);
}
if(p) g_object_unref(p);
return result;
}

// provider string conversion

const char *dt_ai_provider_to_string(dt_ai_provider_t provider)
Expand Down
Loading