Respan Dataset Explorer

Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.

5,167,182physical rows
86shards
0.00%qualified row coverage
0.00%qualified cell coverage
Random row JSON API

turns-00083.parquet:23307

54831554a81977de91874b21
turn 3/10gpt-4.1-mini-2025-04-14EnglishRussia970 words
degenerate_repetitionAbsentFinal dense release
USER
Проверь код - Мне надо на чистом СИ

// - Объявим структуру для хранения контекста модели (описание, исходя из вашего кода):
typedef struct {
    rknn_context ctx;
    rknn_tensor_attr input_attr;
    rknn_tensor_attr output_attrs[3];
    rknn_tensor_mem *input_mem;
    rknn_tensor_mem *output_mems[3];
    int n_output;
    int img_width;
    int img_height;
    std::vector<int32_t> out_zps;
    std::vector<float> out_scales;
} RKNNContext;

// Инициализация RKNN с моделью - почти ваш код из main, упрощенный под нужды потока:
int init_rknn_model(RKNNContext *rknn_ctx, const char *model_path)
{
    memset(rknn_ctx, 0, sizeof(RKNNContext));
    
    int ret = rknn_init(&rknn_ctx->ctx, model_path, 0, 0, NULL);
    if (ret < 0) {
        printf("rknn_init fail! ret=%d\n", ret);
        return -1;
    }

    // Получаем инфо о входах/выходах
    rknn_input_output_num io_num;
    ret = rknn_query(rknn_ctx->ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
    if (ret != RKNN_SUCC) return -1;

    rknn_tensor_attr input_attrs[io_num.n_input];
    for (uint32_t i = 0; i < io_num.n_input; i++) {
        input_attrs[i].index = i;
        ret = rknn_query(rknn_ctx->ctx, RKNN_QUERY_INPUT_ATTR, &input_attrs[i], sizeof(rknn_tensor_attr));
        if (ret != RKNN_SUCC) return -1;
    }

    // Сохраняем только первый input_attr (часто модель один вход)
    rknn_ctx->input_attr = input_attrs[0];
    rknn_ctx->img_width = rknn_ctx->input_attr.dims[2];
    rknn_ctx->img_height = rknn_ctx->input_attr.dims[1];

    // Создаем input_mem (предполагаем NHWC + UINT8, как в вашем коде)
    rknn_ctx->input_attr.type = RKNN_TENSOR_UINT8;
    rknn_ctx->input_attr.fmt = RKNN_TENSOR_NHWC;
    rknn_ctx->input_mem = rknn_create_mem(rknn_ctx->ctx, rknn_ctx->input_attr.size_with_stride);

    // Выходы (возьмем 3 выхода, как в вашем коде)
    rknn_tensor_attr output_attrs[io_num.n_output];
    memset(output_attrs, 0, sizeof(output_attrs));
    for (uint32_t i = 0; i < io_num.n_output; i++) {
        output_attrs[i].index = i;
        ret = rknn_query(rknn_ctx->ctx, RKNN_QUERY_NATIVE_NHWC_OUTPUT_ATTR, &output_attrs[i], sizeof(rknn_tensor_attr));
        if (ret != RKNN_SUCC) return -1;
        rknn_ctx->output_attrs[i] = output_attrs[i];
        rknn_ctx->output_mems[i] = rknn_create_mem(rknn_ctx->ctx, output_attrs[i].size_with_stride);

        rknn_ctx->out_zps.push_back(output_attrs[i].zp);
        rknn_ctx->out_scales.push_back(output_attrs[i].scale);

        ret = rknn_set_io_mem(rknn_ctx->ctx, rknn_ctx->output_mems[i], &rknn_ctx->output_attrs[i]);
        if (ret < 0) return -1;
    }
    rknn_ctx->n_output = io_num.n_output;

    ret = rknn_set_io_mem(rknn_ctx->ctx, rknn_ctx->input_mem, &rknn_ctx->input_attr);
    if (ret < 0) return -1;

    return 0;
}

void release_rknn_model(RKNNContext *rknn_ctx)
{
    if (rknn_ctx->input_mem) rknn_destroy_mem(rknn_ctx->ctx, rknn_ctx->input_mem);
    for (int i = 0; i < rknn_ctx->n_output; i++) {
        if (rknn_ctx->output_mems[i]) rknn_destroy_mem(rknn_ctx->ctx, rknn_ctx->output_mems[i]);
    }
    if (rknn_ctx->ctx) rknn_destroy(rknn_ctx->ctx);
    memset(rknn_ctx, 0, sizeof(RKNNContext));
}

// Функция конвертации из AVFrame RGB24 -> input_mem (предполагаем NHWC uint8)
// input_mem->virt_addr - указатель буфера модели
void resize_and_copy_frame_to_input(AVFrame *rgb_frame, RKNNContext *rknn_ctx)
{
    // Если размер кадра rgb_frame не совпадает с модельным — resize
    // Тут уже rgb_frame — INPUT_WIDTH x INPUT_HEIGHT
    // Если хотите поддерживать разное, надо реализовать resize через stbir_resize_uint8 или sws_scale

    int width = rknn_ctx->img_width;
    int height = rknn_ctx->img_height;
    int channels = 3; // RGB24

    uint8_t *dst_ptr = (uint8_t *)rknn_ctx->input_mem->virt_addr;

    // Если stride == width, копируем напрямую
    int stride = rknn_ctx->input_attr.w_stride;
    if (stride == width) {
        memcpy(dst_ptr, rgb_frame->data[0], width * height * channels);
    } else {
        // копируем построчно с учетом stride
        int src_stride = rgb_frame->linesize[0];
        for (int h = 0; h < height; ++h) {
            memcpy(dst_ptr + h * stride * channels, rgb_frame->data[0] + h * src_stride, width * channels);
        }
    }
}
ASSISTANT
Ваш код — **на C++**, там используются `std::vector`, присваивание структур (копирование `rknn_tensor_attr`), инициализация нулём через `memset` — это все корректно для C, кроме `std::vector`, который — чисто C++ и отсутствует в C.

---

### Главные замечания для перехода на "чистый C":

1. **Нет `std::vector` в C**  
   Нужно заменить `std::vector<int32_t> out_zps;` и `std::vector<float> out_scales;` на обычные массивы или указатели и количество элементов.

2. **Присваивание структур rknn_tensor_attr — допустимо и в C**  
   Структурное присваивание работает в C, поэтому можно оставлять.

3. **Использование `nullptr` / NULL & std::memset — всё в порядке.**

---

## Исправленная версия структуры и функций под чистый C

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rknn_api.h"

// Макс выходов, выставьте по факту у моделей обычно 3
#define MAX_OUTPUT_NUM 3

typedef struct {
    rknn_context ctx;
    rknn_tensor_attr input_attr;
    rknn_tensor_attr output_attrs[MAX_OUTPUT_NUM];
    rknn_tensor_mem *input_mem;
    rknn_tensor_mem *output_mems[MAX_OUTPUT_NUM];
    int n_output;
    int img_width;
    int img_height;
    int32_t out_zps[MAX_OUTPUT_NUM];
    float out_scales[MAX_OUTPUT_NUM];
} RKNNContext;

int init_rknn_model(RKNNContext *rknn_ctx, const char *model_path)
{
    memset(rknn_ctx, 0, sizeof(RKNNContext));

    int ret = rknn_init(&rknn_ctx->ctx, model_path, 0, 0, NULL);
    if (ret < 0) {
        printf("rknn_init fail! ret=%d\n", ret);
        return -1;
    }

    rknn_input_output_num io_num;
    ret = rknn_query(rknn_ctx->ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
    if (ret != RKNN_SUCC) return -1;

    if (io_num.n_input < 1 || io_num.n_output > MAX_OUTPUT_NUM) {
        printf("Invalid input/output num\n");
        return -1;
    }

    // Получаем input_attr (только первый вход)
    rknn_tensor_attr input_attrs[io_num.n_input];
    for (uint32_t i = 0; i < io_num.n_input; i++) {
        input_attrs[i].index = i;
        ret = rknn_query(rknn_ctx->ctx, RKNN_QUERY_INPUT_ATTR, &input_attrs[i], sizeof(rknn_tensor_attr));
        if (ret != RKNN_SUCC) return -1;
    }
    rknn_ctx->input_attr = input_attrs[0];
    rknn_ctx->img_width = rknn_ctx->input_attr.dims[2];
    rknn_ctx->img_height = rknn_ctx->input_attr.dims[1];

    rknn_ctx->input_attr.type = RKNN_TENSOR_UINT8;
    rknn_ctx->input_attr.fmt = RKNN_TENSOR_NHWC;
    rknn_ctx->input_mem = rknn_create_mem(rknn_ctx->ctx, rknn_ctx->input_attr.size_with_stride);
    if (!rknn_ctx->input_mem) {
        printf("Failed to create input mem\n");
        return -1;
    }

    // Получаем выходы
    for (uint32_t i = 0; i < io_num.n_output; i++) {
        rknn_ctx->output_attrs[i].index = i;
        ret = rknn_query(rknn_ctx->ctx, RKNN_QUERY_NATIVE_NHWC_OUTPUT_ATTR, &rknn_ctx->output_attrs[i], sizeof(rknn_tensor_attr));
        if (ret != RKNN_SUCC) return -1;

        rknn_ctx->output_mems[i] = rknn_create_mem(rknn_ctx->ctx, rknn_ctx->output_attrs[i].size_with_stride);
        if (!rknn_ctx->output_mems[i]) {
            printf("Failed to create output mem #%d\n", i);
            return -1;
        }

        ret = rknn_set_io_mem(rknn_ctx->ctx, rknn_ctx->output_mems[i], &rknn_ctx->output_attrs[i]);
        if (ret < 0) return -1;

        rknn_ctx->out_zps[i] = rknn_ctx->output_attrs[i].zp;
        rknn_ctx->out_scales[i] = rknn_ctx->output_attrs[i].scale;
    }

    rknn_ctx->n_output = io_num.n_output;

    // Устанавливаем input мемо
    ret = rknn_set_io_mem(rknn_ctx->ctx, rknn_ctx->input_mem, &rknn_ctx->input_attr);
    if (ret < 0) return -1;

    return 0;
}

void release_rknn_model(RKNNContext *rknn_ctx)
{
    if (rknn_ctx->input_mem)
        rknn_destroy_mem(rknn_ctx->ctx, rknn_ctx->input_mem);

    for (int i = 0; i < rknn_ctx->n_output; i++) {
        if (rknn_ctx->output_mems[i])
            rknn_destroy_mem(rknn_ctx->ctx, rknn_ctx->output_mems[i]);
    }

    if (rknn_ctx->ctx)
        rknn_destroy(rknn_ctx->ctx);

    memset(rknn_ctx, 0, sizeof(RKNNContext));
}

void resize_and_copy_frame_to_input(AVFrame *rgb_frame, RKNNContext *rknn_ctx)
{
    int width = rknn_ctx->img_width;
    int height = rknn_ctx->img_height;
    int channels = 3; // RGB24
    uint8_t *dst_ptr = (uint8_t *)rknn_ctx->input_mem->virt_addr;
    int stride = rknn_ctx->input_attr.w_stride;

    if (stride == width) {
        memcpy(dst_ptr, rgb_frame->data[0], width * height * channels);
    } else {
        int src_stride = rgb_frame->linesize[0];
        for (int h = 0; h < height; ++h) {
            memcpy(dst_ptr + h * stride * channels, rgb_frame->data[0] + h * src_stride, width * channels);
        }
    }
}
```

---

### Ключевые моменты:

- Заменены `std::vector` на обычные массивы с `MAX_OUTPUT_NUM` длиной (заранее известное число выходов — обычно 3).
- Использован `int32_t` и `float` для массивов под `out_zps` и `out_scales`.
- Добавлена проверка на количество входов/выходов.
- Добавлены проверки на успешное выделение памяти.

---

Если нужен пример кода вызова RKNN в таком стиле (например, в цикле обработки кадров) — могу добавить.

---

### Итог

**Ваш код на чистом СИ — с учётом исправления `std::vector` — приведён выше.**  
Его можно смело использовать в вашем проекте с FFmpeg и RKNN.

turns-00083.parquet:23308

be061353a0193c0c405d12c9
turn 4/10gpt-4.1-mini-2025-04-14EnglishRussia3490 words
degenerate_repetitionAbsentFinal dense release
USER
Теперь тут на чистом СИ 
// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <sys/time.h>
#include <vector>
#include <set>
#include "postprocess.h"
#include <stdint.h>
#define LABEL_NALE_TXT_PATH "./model/coco_80_labels_list.txt"

static char *labels[OBJ_CLASS_NUM];

const int anchor0[6] = {10, 13, 16, 30, 33, 23};
const int anchor1[6] = {30, 61, 62, 45, 59, 119};
const int anchor2[6] = {116, 90, 156, 198, 373, 326};

inline static int clamp(float val, int min, int max)
{
    return val > min ? (val < max ? val : max) : min;
}

char *readLine(FILE *fp, char *buffer, int *len)
{
    int ch;
    int i = 0;
    size_t buff_len = 0;

    buffer = (char *)malloc(buff_len + 1);
    if (!buffer)
        return NULL; // Out of memory

    while ((ch = fgetc(fp)) != '\n' && ch != EOF)
    {
        buff_len++;
        void *tmp = realloc(buffer, buff_len + 1);
        if (tmp == NULL)
        {
            free(buffer);
            return NULL; // Out of memory
        }
        buffer = (char *)tmp;

        buffer[i] = (char)ch;
        i++;
    }
    buffer[i] = '\0';

    *len = buff_len;

    // Detect end
    if (ch == EOF && (i == 0 || ferror(fp)))
    {
        free(buffer);
        return NULL;
    }
    return buffer;
}

int readLines(const char *fileName, char *lines[], int max_line)
{
    FILE *file = fopen(fileName, "r");
    char *s;
    int i = 0;
    int n = 0;
    while ((s = readLine(file, s, &n)) != NULL)
    {
        lines[i++] = s;
        if (i >= max_line)
            break;
    }
    return i;
}

int loadLabelName(const char *locationFilename, char *label[])
{
    printf("loadLabelName %s\n", locationFilename);
    readLines(locationFilename, label, OBJ_CLASS_NUM);
    return 0;
}

static float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1, float ymax1)
{
    float w = fmax(0.f, fmin(xmax0, xmax1) - fmax(xmin0, xmin1) + 1.0);
    float h = fmax(0.f, fmin(ymax0, ymax1) - fmax(ymin0, ymin1) + 1.0);
    float i = w * h;
    float u = (xmax0 - xmin0 + 1.0) * (ymax0 - ymin0 + 1.0) + (xmax1 - xmin1 + 1.0) * (ymax1 - ymin1 + 1.0) - i;
    return u <= 0.f ? 0.f : (i / u);
}

static int nms(int validCount, std::vector<float> &outputLocations, std::vector<int> classIds, std::vector<int> &order,int filterId, float threshold)
{
    for (int i = 0; i < validCount; ++i)
    {
        if (order[i] == -1|| classIds[i] != filterId)
        {
            continue;
        }
        int n = order[i];
        for (int j = i + 1; j < validCount; ++j)
        {
            int m = order[j];
            if (m == -1 || classIds[i] != filterId)
            {
                continue;
            }
            float xmin0 = outputLocations[n * 4 + 0];
            float ymin0 = outputLocations[n * 4 + 1];
            float xmax0 = outputLocations[n * 4 + 0] + outputLocations[n * 4 + 2];
            float ymax0 = outputLocations[n * 4 + 1] + outputLocations[n * 4 + 3];

            float xmin1 = outputLocations[m * 4 + 0];
            float ymin1 = outputLocations[m * 4 + 1];
            float xmax1 = outputLocations[m * 4 + 0] + outputLocations[m * 4 + 2];
            float ymax1 = outputLocations[m * 4 + 1] + outputLocations[m * 4 + 3];

            float iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1);

            if (iou > threshold)
            {
                order[j] = -1;
            }
        }
    }
    return 0;
}

static int quick_sort_indice_inverse(
    std::vector<float> &input,
    int left,
    int right,
    std::vector<int> &indices)
{
    float key;
    int key_index;
    int low = left;
    int high = right;
    if (left < right)
    {
        key_index = indices[left];
        key = input[left];
        while (low < high)
        {
            while (low < high && input[high] <= key)
            {
                high--;
            }
            input[low] = input[high];
            indices[low] = indices[high];
            while (low < high && input[low] >= key)
            {
                low++;
            }
            input[high] = input[low];
            indices[high] = indices[low];
        }
        input[low] = key;
        indices[low] = key_index;
        quick_sort_indice_inverse(input, left, low - 1, indices);
        quick_sort_indice_inverse(input, low + 1, right, indices);
    }
    return low;
}

static float sigmoid(float x)
{
    return 1.0 / (1.0 + expf(-x));
}

static float unsigmoid(float y)
{
    return -1.0 * logf((1.0 / y) - 1.0);
}

inline static int32_t __clip(float val, float min, float max)
{
    float f = val <= min ? min : (val >= max ? max : val);
    return f;
}

static int8_t qnt_f32_to_affine(float f32, int32_t zp, float scale)
{
    float dst_val = (f32 / scale) + zp;
    int8_t res = (int8_t)__clip(dst_val, -128, 127);
    return res;
}

static float deqnt_affine_to_f32(int8_t qnt, int32_t zp, float scale)
{
    return ((float)qnt - (float)zp) * scale;
}

static int process(int8_t *input, int *anchor, int grid_h, int grid_w, int height, int width, int stride,
                   std::vector<float> &boxes, std::vector<float> &objProbs, std::vector<int> &classId,
                   float threshold, int32_t zp, float scale)
{

    int validCount = 0;
    int grid_len = grid_h * grid_w;
    int8_t thres_i8 = qnt_f32_to_affine(threshold, zp, scale);
    for (int a = 0; a < 3; a++)
    {
        for (int i = 0; i < grid_h; i++)
        {
            for (int j = 0; j < grid_w; j++)
            {
                int8_t box_confidence = input[(PROP_BOX_SIZE * a + 4) * grid_len + i * grid_w + j];
                if (box_confidence >= thres_i8)
                {
                    int offset = (PROP_BOX_SIZE * a) * grid_len + i * grid_w + j;
                    int8_t *in_ptr = input + offset;
                    float box_x = (deqnt_affine_to_f32(*in_ptr, zp, scale)) * 2.0 - 0.5;
                    float box_y = (deqnt_affine_to_f32(in_ptr[grid_len], zp, scale)) * 2.0 - 0.5;
                    float box_w = (deqnt_affine_to_f32(in_ptr[2 * grid_len], zp, scale)) * 2.0;
                    float box_h = (deqnt_affine_to_f32(in_ptr[3 * grid_len], zp, scale)) * 2.0;
                    box_x = (box_x + j) * (float)stride;
                    box_y = (box_y + i) * (float)stride;
                    box_w = box_w * box_w * (float)anchor[a * 2];
                    box_h = box_h * box_h * (float)anchor[a * 2 + 1];
                    box_x -= (box_w / 2.0);
                    box_y -= (box_h / 2.0);

                    int8_t maxClassProbs = in_ptr[5 * grid_len];
                    int maxClassId = 0;
                    for (int k = 1; k < OBJ_CLASS_NUM; ++k)
                    {
                        int8_t prob = in_ptr[(5 + k) * grid_len];
                        if (prob > maxClassProbs)
                        {
                            maxClassId = k;
                            maxClassProbs = prob;
                        }
                    }
                    if (maxClassProbs>thres_i8){
                        boxes.push_back(box_x);
                        boxes.push_back(box_y);
                        boxes.push_back(box_w);
                        boxes.push_back(box_h);
                        objProbs.push_back((deqnt_affine_to_f32(maxClassProbs, zp, scale))* (deqnt_affine_to_f32(box_confidence, zp, scale)));
                        classId.push_back(maxClassId);
                        validCount++;
                    }
                }
            }
        }
    }
    return validCount;
}


static int process_native_nhwc(int8_t *input, int *anchor, int grid_h, int grid_w, int height, int width, int stride,
                   std::vector<float> &boxes, std::vector<float> &boxScores, std::vector<int> &classId,
                   float threshold, int32_t zp, float scale)
{
    int validCount = 0;
    int8_t thres_i8 = qnt_f32_to_affine(threshold, zp, scale);

    int anchor_per_branch = 3;
    // 新驱动不再有对齐要求
    // int align_c = get_align(PROP_BOX_SIZE*anchor_per_branch, 16);
    int align_c = PROP_BOX_SIZE*anchor_per_branch;
    // printf("align_c %d\n", align_c);

    for (int h=0; h < grid_h; h++){
        for (int w=0; w < grid_w; w++){
            for (int a=0; a < anchor_per_branch; a++){
                int hw_offset = h*grid_w*align_c + w*align_c + a*PROP_BOX_SIZE;
                // int hw_offset = h*grid_w*anchor_per_branch*PROP_BOX_SIZE + w*anchor_per_branch*PROP_BOX_SIZE + a*PROP_BOX_SIZE;
                int8_t *hw_ptr = input + hw_offset;
                int8_t box_confidence = hw_ptr[4];
                
                if (box_confidence >= thres_i8){
                    // printf("box_conf %d, thres_i8 %d\n", box_confidence, thres_i8);

                    int8_t maxClassProbs = hw_ptr[5];
                    int maxClassId = 0;
                    for (int k = 1; k < OBJ_CLASS_NUM; ++k)
                    {
                        int8_t prob = hw_ptr[5 + k];
                        if (prob > maxClassProbs)
                        {
                            maxClassId = k;
                            maxClassProbs = prob;
                        }
                    }

                    // printf("box_conf %d, thres_i8 %d, maxClassProbs %d\n", box_confidence, thres_i8, maxClassProbs);
                    float box_conf_f32 = deqnt_affine_to_f32(box_confidence, zp, scale);
                    float class_prob_f32 = deqnt_affine_to_f32(maxClassProbs, zp, scale);
                    float limit_score = box_conf_f32* class_prob_f32;

                    if (limit_score > threshold){
                        float box_x, box_y, box_w, box_h;

                        box_x = deqnt_affine_to_f32(hw_ptr[0], zp, scale) * 2.0 - 0.5;
                        box_y = deqnt_affine_to_f32(hw_ptr[1], zp, scale) * 2.0 - 0.5;
                        box_w = deqnt_affine_to_f32(hw_ptr[2], zp, scale) * 2.0;
                        box_h = deqnt_affine_to_f32(hw_ptr[3], zp, scale) * 2.0;
                        box_w = box_w * box_w;
                        box_h = box_h * box_h;


                        box_x = (box_x + w) * (float)stride;
                        box_y = (box_y + h) * (float)stride;
                        box_w *= (float)anchor[a * 2];
                        box_h *= (float)anchor[a * 2 + 1];

                        box_x -= (box_w / 2.0);
                        box_y -= (box_h / 2.0);

                        boxes.push_back(box_x);
                        boxes.push_back(box_y);
                        boxes.push_back(box_w);
                        boxes.push_back(box_h);
                        boxScores.push_back(limit_score);
                        classId.push_back(maxClassId);
                        validCount++;
                    }
                }
            }
        }
    }
    return validCount;
}

int post_process(int8_t *input0, int8_t *input1, int8_t *input2, int model_in_h, int model_in_w,
                 float conf_threshold, float nms_threshold, float scale_w, float scale_h,
                 std::vector<int32_t> &qnt_zps, std::vector<float> &qnt_scales,
                 detect_result_group_t *group)
{
    static int init = -1;
    if (init == -1)
    {
        int ret = 0;
        ret = loadLabelName(LABEL_NALE_TXT_PATH, labels);
        if (ret < 0)
        {
            return -1;
        }

        init = 0;
    }
    memset(group, 0, sizeof(detect_result_group_t));

    std::vector<float> filterBoxes;
    std::vector<float> objProbs;
    std::vector<int> classId;
    
    // stride 8
    int stride0 = 8;
    int grid_h0 = model_in_h / stride0;
    int grid_w0 = model_in_w / stride0;
    int validCount0 = 0;
    validCount0 = process_native_nhwc(input0, (int *)anchor0, grid_h0, grid_w0, model_in_h, model_in_w,
                          stride0, filterBoxes, objProbs, classId, conf_threshold, qnt_zps[0], qnt_scales[0]);

    // stride 16
    int stride1 = 16;
    int grid_h1 = model_in_h / stride1;
    int grid_w1 = model_in_w / stride1;
    int validCount1 = 0;
    validCount1 = process_native_nhwc(input1, (int *)anchor1, grid_h1, grid_w1, model_in_h, model_in_w,
                          stride1, filterBoxes, objProbs, classId, conf_threshold, qnt_zps[1], qnt_scales[1]);

    // stride 32
    int stride2 = 32;
    int grid_h2 = model_in_h / stride2;
    int grid_w2 = model_in_w / stride2;
    int validCount2 = 0;
    validCount2 = process_native_nhwc(input2, (int *)anchor2, grid_h2, grid_w2, model_in_h, model_in_w,
                          stride2, filterBoxes, objProbs, classId, conf_threshold, qnt_zps[2], qnt_scales[2]);

    int validCount = validCount0 + validCount1 + validCount2;
    // no object detect
    if (validCount <= 0)
    {
        return 0;
    }

    std::vector<int> indexArray;
    for (int i = 0; i < validCount; ++i)
    {
        indexArray.push_back(i);
    }

    quick_sort_indice_inverse(objProbs, 0, validCount - 1, indexArray);

    std::set<int> class_set(std::begin(classId),std::end(classId));
    
    for(auto c : class_set){
        nms(validCount, filterBoxes, classId, indexArray, c, nms_threshold);
    }

    int last_count = 0;
    group->count = 0;
    /* box valid detect target */
    for (int i = 0; i < validCount; ++i)
    {

        if (indexArray[i] == -1 || last_count >= OBJ_NUMB_MAX_SIZE)
        {
            continue;
        }
        int n = indexArray[i];

        float x1 = filterBoxes[n * 4 + 0];
        float y1 = filterBoxes[n * 4 + 1];
        float x2 = x1 + filterBoxes[n * 4 + 2];
        float y2 = y1 + filterBoxes[n * 4 + 3];
        int id = classId[n];
        float obj_conf = objProbs[i];

        group->results[last_count].box.left = (int)(clamp(x1, 0, model_in_w) / scale_w);
        group->results[last_count].box.top = (int)(clamp(y1, 0, model_in_h) / scale_h);
        group->results[last_count].box.right = (int)(clamp(x2, 0, model_in_w) / scale_w);
        group->results[last_count].box.bottom = (int)(clamp(y2, 0, model_in_h) / scale_h);
        group->results[last_count].prop = obj_conf;
        char *label = labels[id];
        strncpy(group->results[last_count].name, label, OBJ_NAME_MAX_SIZE);

        // printf("result %2d: (%4d, %4d, %4d, %4d), %s\n", i, group->results[last_count].box.left, group->results[last_count].box.top,
        //        group->results[last_count].box.right, group->results[last_count].box.bottom, label);
        last_count++;
    }
    group->count = last_count;

    return 0;
}
ASSISTANT
Ниже представлен пример перевода вашего кода (который использует std::vector, std::set и другие C++ конструкции) в **чистый C** без STL, с использованием динамических массивов через malloc/realloc и наивной реализации множества классов для NMS.

---

# Ключевые идеи перехода на чистый C:

- Заменяем `std::vector<float>` на динамический массив `float*` с контролем размера и длины.
- Заменяем `std::vector<int>` на `int*`.
- Вместо `std::set<int>` можно использовать простой массив с проверкой уникальности.
- Рекурсивный `quick_sort_indice_inverse` реализуем с массивами.
- Убираем использование `inline static` — заменяем на `static` (возможно `inline` поднимается компилятором).
- Используем C-комментарии и строгий стиль C.
- Для `strncpy` выводим просто как есть, убедитесь что `OBJ_NAME_MAX_SIZE` и `OBJ_CLASS_NUM` определены.

---

# Полный пример на чистом C:

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdint.h>

#define LABEL_NALE_TXT_PATH "./model/coco_80_labels_list.txt"
#define OBJ_CLASS_NUM 80
#define OBJ_NAME_MAX_SIZE 64
#define OBJ_NUMB_MAX_SIZE 512
#define PROP_BOX_SIZE 85 // Обычно 5 + 80 классов

typedef struct {
    int left, top, right, bottom;
} box_t;

typedef struct {
    char name[OBJ_NAME_MAX_SIZE];
    box_t box;
    float prop;
} detect_result_t;

typedef struct {
    detect_result_t results[OBJ_NUMB_MAX_SIZE];
    int count;
} detect_result_group_t;

static char *labels[OBJ_CLASS_NUM];

// якоря
static const int anchor0[6] = {10, 13, 16, 30, 33, 23};
static const int anchor1[6] = {30, 61, 62, 45, 59, 119};
static const int anchor2[6] = {116, 90, 156, 198, 373, 326};

static int clamp_i(float val, int min, int max)
{
    if (val < min) return min;
    if (val > max) return max;
    return (int)val;
}

// --- динамические массивы ---------------------------------------------------
typedef struct {
    float *data;
    int count;
    int capacity;
} float_array;

typedef struct {
    int *data;
    int count;
    int capacity;
} int_array;

static void float_array_init(float_array *arr)
{
    arr->data = NULL;
    arr->count = 0;
    arr->capacity = 0;
}

static void float_array_free(float_array *arr)
{
    if (arr->data) free(arr->data);
    arr->data = NULL;
    arr->count = 0;
    arr->capacity = 0;
}

static int float_array_push(float_array *arr, float val)
{
    if (arr->count >= arr->capacity)
    {
        int new_cap = arr->capacity == 0 ? 64 : arr->capacity * 2;
        float *tmp = (float *)realloc(arr->data, new_cap * sizeof(float));
        if (!tmp) return -1;
        arr->data = tmp;
        arr->capacity = new_cap;
    }
    arr->data[arr->count++] = val;
    return 0;
}

static void int_array_init(int_array *arr)
{
    arr->data = NULL;
    arr->count = 0;
    arr->capacity = 0;
}

static void int_array_free(int_array *arr)
{
    if (arr->data) free(arr->data);
    arr->data = NULL;
    arr->count = 0;
    arr->capacity = 0;
}

static int int_array_push(int_array *arr, int val)
{
    if (arr->count >= arr->capacity)
    {
        int new_cap = arr->capacity == 0 ? 64 : arr->capacity * 2;
        int *tmp = (int *)realloc(arr->data, new_cap * sizeof(int));
        if (!tmp) return -1;
        arr->data = tmp;
        arr->capacity = new_cap;
    }
    arr->data[arr->count++] = val;
    return 0;
}

// --- конец динамических массивов ---------------------------------------------

// Функция чтения строк и загрузки меток в labels - оставляйте вашу реализацию

// CalculateOverlap
static float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0,
                              float xmin1, float ymin1, float xmax1, float ymax1)
{
    float w = fmaxf(0.f, fminf(xmax0, xmax1) - fmaxf(xmin0, xmin1) + 1.0f);
    float h = fmaxf(0.f, fminf(ymax0, ymax1) - fmaxf(ymin0, ymin1) + 1.0f);
    float i = w * h;
    float u = (xmax0 - xmin0 + 1.0f) * (ymax0 - ymin0 + 1.0f) + (xmax1 - xmin1 + 1.0f) * (ymax1 - ymin1 + 1.0f) - i;
    return u <= 0.f ? 0.f : (i / u);
}

// NMS с динамическими массивами
static void nms(int validCount, float_array *outputLocations, int_array *classIds, int *order, int filterId, float threshold)
{
    for (int i = 0; i < validCount; ++i)
    {
        if (order[i] == -1 || classIds->data[i] != filterId)
            continue;
        int n = order[i];
        for (int j = i + 1; j < validCount; ++j)
        {
            int m = order[j];
            if (m == -1 || classIds->data[j] != filterId)
                continue;

            float xmin0 = outputLocations->data[n * 4 + 0];
            float ymin0 = outputLocations->data[n * 4 + 1];
            float xmax0 = outputLocations->data[n * 4 + 0] + outputLocations->data[n * 4 + 2];
            float ymax0 = outputLocations->data[n * 4 + 1] + outputLocations->data[n * 4 + 3];

            float xmin1 = outputLocations->data[m * 4 + 0];
            float ymin1 = outputLocations->data[m * 4 + 1];
            float xmax1 = outputLocations->data[m * 4 + 0] + outputLocations->data[m * 4 + 2];
            float ymax1 = outputLocations->data[m * 4 + 1] + outputLocations->data[m * 4 + 3];

            float iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1);

            if (iou > threshold)
                order[j] = -1;
        }
    }
}

// Быстрая сортировка с массивами
static void quick_sort_indice_inverse(float_array *input, int left, int right, int *indices)
{
    if (left >= right)
        return;

    float key = input->data[left];
    int key_index = indices[left];
    int low = left;
    int high = right;

    while (low < high)
    {
        while (low < high && input->data[high] <= key) --high;
        input->data[low] = input->data[high];
        indices[low] = indices[high];

        while (low < high && input->data[low] >= key) ++low;
        input->data[high] = input->data[low];
        indices[high] = indices[low];
    }

    input->data[low] = key;
    indices[low] = key_index;

    quick_sort_indice_inverse(input, left, low - 1, indices);
    quick_sort_indice_inverse(input, low + 1, right, indices);
}

static int8_t qnt_f32_to_affine(float f32, int32_t zp, float scale)
{
    float dst_val = (f32 / scale) + zp;
    if (dst_val < -128) dst_val = -128;
    if (dst_val > 127) dst_val = 127;
    return (int8_t)dst_val;
}

static float deqnt_affine_to_f32(int8_t qnt, int32_t zp, float scale)
{
    return ((float)qnt - (float)zp) * scale;
}

// Пример функции process_native_nhwc (адаптация под C, с dynamic arrays).
// Добавляйте аналогично process() и остальные функции по образцу.

static int process_native_nhwc(int8_t *input, int *anchor, int grid_h, int grid_w,
                              int height, int width, int stride,
                              float_array *boxes, float_array *boxScores,
                              int_array *classId,
                              float threshold, int32_t zp, float scale)
{
    int validCount = 0;
    int8_t thres_i8 = qnt_f32_to_affine(threshold, zp, scale);
    int anchor_per_branch = 3;
    int align_c = PROP_BOX_SIZE * anchor_per_branch;

    for (int h=0; h < grid_h; h++){
        for (int w=0; w < grid_w; w++){
            for (int a=0; a < anchor_per_branch; a++){
                int hw_offset = h * grid_w * align_c + w * align_c + a * PROP_BOX_SIZE;
                int8_t *hw_ptr = input + hw_offset;
                int8_t box_confidence = hw_ptr[4];
                if (box_confidence >= thres_i8){
                    int8_t maxClassProbs = hw_ptr[5];
                    int maxClassId = 0;
                    for (int k = 1; k < OBJ_CLASS_NUM; ++k)
                    {
                        int8_t prob = hw_ptr[5 + k];
                        if (prob > maxClassProbs)
                        {
                            maxClassId = k;
                            maxClassProbs = prob;
                        }
                    }
                    float box_conf_f32 = deqnt_affine_to_f32(box_confidence, zp, scale);
                    float class_prob_f32 = deqnt_affine_to_f32(maxClassProbs, zp, scale);
                    float limit_score = box_conf_f32 * class_prob_f32;
                    if (limit_score > threshold){
                        float box_x = deqnt_affine_to_f32(hw_ptr[0], zp, scale) * 2.0f - 0.5f;
                        float box_y = deqnt_affine_to_f32(hw_ptr[1], zp, scale) * 2.0f - 0.5f;
                        float box_w = deqnt_affine_to_f32(hw_ptr[2], zp, scale) * 2.0f;
                        float box_h = deqnt_affine_to_f32(hw_ptr[3], zp, scale) * 2.0f;
                        box_w = box_w * box_w;
                        box_h = box_h * box_h;
                        box_x = (box_x + w) * (float)stride;
                        box_y = (box_y + h) * (float)stride;
                        box_w *= (float)anchor[a * 2];
                        box_h *= (float)anchor[a * 2 + 1];
                        box_x -= box_w / 2.0f;
                        box_y -= box_h / 2.0f;

                        // Добавляем в динамические массивы
                        if (float_array_push(boxes, box_x) < 0) return -1;
                        if (float_array_push(boxes, box_y) < 0) return -1;
                        if (float_array_push(boxes, box_w) < 0) return -1;
                        if (float_array_push(boxes, box_h) < 0) return -1;
                        if (float_array_push(boxScores, limit_score) < 0) return -1;
                        if (int_array_push(classId, maxClassId) < 0) return -1;

                        validCount++;
                    }
                }
            }
        }
    }
    return validCount;
}

// Аналогично напишите функции process() и остальные...

// Пример реализации post_process

int post_process(int8_t *input0, int8_t *input1, int8_t *input2,
                 int model_in_h, int model_in_w,
                 float conf_threshold, float nms_threshold,
                 float scale_w, float scale_h,
                 int32_t *qnt_zps, float *qnt_scales,
                 detect_result_group_t *group)
{
    static int init = -1;
    if (init == -1)
    {
        int ret = loadLabelName(LABEL_NALE_TXT_PATH, labels);
        if (ret < 0) return -1;
        init = 0;
    }

    memset(group, 0, sizeof(detect_result_group_t));

    // Инициализация динамических массивов
    float_array filterBoxes; float_array_init(&filterBoxes);
    float_array objProbs; float_array_init(&objProbs);
    int_array classId; int_array_init(&classId);

    // Обрабатываем выходы (stride 8,16,32)
    int grid_h, grid_w;
    int validCount0 = 0, validCount1 = 0, validCount2 = 0;

    grid_h = model_in_h / 8;
    grid_w = model_in_w / 8;
    validCount0 = process_native_nhwc(input0, (int *)anchor0, grid_h, grid_w,
                                     model_in_h, model_in_w, 8,
                                     &filterBoxes, &objProbs, &classId,
                                     conf_threshold, qnt_zps[0], qnt_scales[0]);

    grid_h = model_in_h / 16;
    grid_w = model_in_w / 16;
    validCount1 = process_native_nhwc(input1, (int *)anchor1, grid_h, grid_w,
                                     model_in_h, model_in_w, 16,
                                     &filterBoxes, &objProbs, &classId,
                                     conf_threshold, qnt_zps[1], qnt_scales[1]);

    grid_h = model_in_h / 32;
    grid_w = model_in_w / 32;
    validCount2 = process_native_nhwc(input2, (int *)anchor2, grid_h, grid_w,
                                     model_in_h, model_in_w, 32,
                                     &filterBoxes, &objProbs, &classId,
                                     conf_threshold, qnt_zps[2], qnt_scales[2]);

    int validCount = validCount0 + validCount1 + validCount2;
    if (validCount <= 0)
    {
        float_array_free(&filterBoxes);
        float_array_free(&objProbs);
        int_array_free(&classId);
        return 0;
    }

    // Создаем массив индексов для сортировки
    int *indexArray = (int *)malloc(validCount * sizeof(int));
    if (!indexArray)
    {
        float_array_free(&filterBoxes);
        float_array_free(&objProbs);
        int_array_free(&classId);
        return -1;
    }
    for (int i = 0; i < validCount; ++i)
        indexArray[i] = i;

    // Сортировка
    quick_sort_indice_inverse(&objProbs, 0, validCount - 1, indexArray);

    // Собираем уникальные классы в массив
    int *unique_classes = (int *)malloc(validCount * sizeof(int));
    int unique_count = 0;
    for (int i = 0; i < validCount; ++i)
    {
        int c = classId.data[i];
        int exists = 0;
        for (int j = 0; j < unique_count; ++j)
            if (unique_classes[j] == c) {
                exists = 1;
                break;
            }
        if (!exists)
            unique_classes[unique_count++] = c;
    }

    // NMS для каждого класса
    for (int cidx = 0; cidx < unique_count; ++cidx)
    {
        nms(validCount, &filterBoxes, &classId, indexArray, unique_classes[cidx], nms_threshold);
    }
    free(unique_classes);

    int last_count = 0;
    group->count = 0;
    // Копируем результаты
    for (int i = 0; i < validCount; ++i)
    {
        int idx = indexArray[i];
        if (idx == -1 || last_count >= OBJ_NUMB_MAX_SIZE)
            continue;

        float x1 = filterBoxes.data[idx * 4 + 0];
        float y1 = filterBoxes.data[idx * 4 + 1];
        float x2 = x1 + filterBoxes.data[idx * 4 + 2];
        float y2 = y1 + filterBoxes.data[idx * 4 + 3];
        int id = classId.data[idx];
        float obj_conf = objProbs.data[i];

        group->results[last_count].box.left = clamp_i(x1 / scale_w, 0, model_in_w);
        group->results[last_count].box.top = clamp_i(y1 / scale_h, 0, model_in_h);
        group->results[last_count].box.right = clamp_i(x2 / scale_w, 0, model_in_w);
        group->results[last_count].box.bottom = clamp_i(y2 / scale_h, 0, model_in_h);
        group->results[last_count].prop = obj_conf;
        if (id >= 0 && id < OBJ_CLASS_NUM && labels[id])
            strncpy(group->results[last_count].name, labels[id], OBJ_NAME_MAX_SIZE - 1);
        group->results[last_count].name[OBJ_NAME_MAX_SIZE-1] = 0;

        last_count++;
    }
    group->count = last_count;

    free(indexArray);
    float_array_free(&filterBoxes);
    float_array_free(&objProbs);
    int_array_free(&classId);

    return 0;
}
```

---

### Пояснения:

- Заменили `std::vector` на `float_array` и `int_array` — динамические массивы с добавлением/удалением.
- Вместо `std::set` — простой наивный поиск уникальных элементов в массиве.
- Вместо `for(auto c : class_set)` — итерация по массиву уникальных классов.
- Добавлен контроль ошибок при malloc/realloc.
- Стандартные библиотечные функции `malloc`, `realloc`, `free`, `memset`, `strncpy` используются как есть.
- Необходимо реализовать `loadLabelName` и `readLine` отдельно(выше ваш код уже на чистом C).
- Убедитесь, что массивы `labels[]`, `anchor0`, `anchor1`, `anchor2` объявлены глобально и видны.
- Проверьте что `PROP_BOX_SIZE`, `OBJ_CLASS_NUM`, `OBJ_NUMB_MAX_SIZE`, `OBJ_NAME_MAX_SIZE` объявлены и имеют правильные значения.
- Функции `process()` и `process_native_nhwc()` можно привести в аналогичный стиль.

---

Если нужно — могу помочь с доработкой отдельных функций.  
Это достаточное и рабочее направление для портирования вашего C++ кода в чистый C с минимально необходимыми изменениями под отсутствие STL.

turns-00083.parquet:23309

24e64e9b3730e235c304e49a
turn 5/10gpt-4.1-mini-2025-04-14EnglishRussia320 words
degenerate_repetitionAbsentFinal dense release
USER
как убрать вывод x25a10] CSeq 312 expected, 301 received.
[rtsp @ 0x25a10] CSeq 312 expected, 302 received.
[rtsp @ 0x25a10] CSeq 312 expected, 307 received.
[rtsp @ 0x25a10] CSeq 312 expected, 311 received.
[rtsp @ 0x25a10] CSeq 322 expected, 313 received.
[rtsp @ 0x25a10] CSeq 322 expected, 314 received.
[rtsp @ 0x25a10] CSeq 322 expected, 318 received.
[rtsp @ 0x25a10] CSeq 331 expected, 323 received.
[rtsp @ 0x25a10] CSeq 331 expected, 324 received.
[rtsp @ 0x25a10] CSeq 331 expected, 329 received.
[rtsp @ 0x25a10] CSeq 343 expected, 332 receiv
ASSISTANT
Это сообщения от ffmpeg библиотеки (libavformat) об ошибках RTSP — warnings и info, которые выводятся в stderr через внутренний логгер.

Чтобы их подавить или отключить, есть несколько способов:

---

### Способ 1. Установить уровень логирования FFmpeg в коде

В начале вашей программы (после инициализации ffmpeg, обычно сразу после `avformat_network_init()`) добавьте

```c
av_log_set_level(AV_LOG_QUIET);  // Полное отключение вывода логов ffmpeg
```

Или более мягкий уровень (например, WARN или ERROR):

```c
av_log_set_level(AV_LOG_ERROR);
```

Чем выше уровень, тем меньше логов.

---

### Способ 2. Установить собственный лог-функцию, которая ничего не выводит

```c
void null_log_callback(void* ptr, int level, const char* fmt, va_list vl) {
    // Ничего не делаем — лог игнорируем
}

int main()
{
    av_log_set_callback(null_log_callback);
    // далее ваша инициализация и код
}
```

---

### Способ 3. Обычно в команде ffmpeg CLI или при настройке AVFormatContext можно задать опцию `-loglevel quiet` — в API это через `av_log_set_level()`.

---

### Заключение

Для вашего случая достаточно вызова

```c
av_log_set_level(AV_LOG_ERROR);
```

чтобы скрыть предупреждения типа "[rtsp @ 0x25a10] CSeq ... expected ... received" и выводить только ошибки (или AV_LOG_QUIET, если хотите полностью).

---

### Пример в коде

```c
int main(int argc, char *argv[]) {
    avformat_network_init();

    av_log_set_level(AV_LOG_ERROR);

    // ваш остальной код
}
```

---

Если лог нужно «переключать» динамически — используйте `av_log_set_level()`.

---

Если хотите подавить отдельные сообщения, но оставить остальные — придется писать кастомную функцию логирования и фильтровать по сообщению (в `av_log_set_callback()`).

---

Если нужна помощь — пишите!

turns-00083.parquet:23310

e0859bf57d1215160ff7ac5c
turn 6/10gpt-4.1-mini-2025-04-14EnglishRussia417 words
degenerate_repetitionAbsentFinal dense release
USER
а надо делать  sws_scale если мы отдаем resize_and_copy_frame_to_input - может сразу отдать frame->data
	sws_scale(
												sws_ctx,
												(const uint8_t * const *)frame->data, frame->linesize,
												0, frame->height,
												rgb_frame->data, rgb_frame->linesize);



										int64_t start_us = getCurrentTimeUs();
										resize_and_copy_frame_to_input(rgb_frame, &rknn_ctx);
										int64_t elapse_us = getCurrentTimeUs() - start_us;
ASSISTANT
Если модель ожидает кадр определённого размера (например, 640x640) и пиксельного формата (чаще RGB24 в вашем случае), а `frame` у вас из декодера — обычно в формате YUV (например, YUV420P) и другом разрешении, то:

---

### Зачем нужен `sws_scale`?

`avcodec_receive_frame()` возвращает кадр в формате сырого видео (чаще YUV).

Модель RKNN обычно принимает RGB24 (или другой), и строго фиксированный размер.

`rgb_frame` — это выделенный под RGB кадровый буфер нужного размера.

`sws_scale()` конвертирует и масштабирует из исходного формата (YUV + оригинальный размер) → RGB24 + нужный размер.

После чего `rgb_frame` готов для передачи в RKNN.

---

### Можно ли передать напрямую из `frame->data` в `resize_and_copy_frame_to_input`?

Нет, **нельзя**, потому что:

- `frame->data` — в YUV формате, НЕ RGB.

- `frame->data` мог иметь любой размер и формат, отличающийся от модели.

---

### Можно ли не делать `resize_and_copy_frame_to_input` и передавать `rgb_frame->data` напрямую?

- **Можно**, если:

   - Ваша модель принимает вход строго в формате и размере, соответствующем `rgb_frame`.

   - При этом вы укажете RKNN нужный формат и размер.

- `resize_and_copy_frame_to_input()` обычно делает только копирование из `rgb_frame->data` (который уже 640x640 RGB) в отдельный буфер RKNN входа.

Если `rgb_frame->data` и RKNN вход (input_mem) совпадают по размеру, формату и памяти (например, zero-copy), то можно попробовать передавать напрямую или указать RKNN использовать этот буфер.

---

### Если `resize_and_copy_frame_to_input` только копирует и не делает ресайза

Это нормально — ресайз и конвертация делается **один раз в `sws_scale`**, а копирование — чтобы положить данные в выделенный для RKNN буфер.

---

### Вывод:

- `sws_scale` — **обязателен** для конвертации из оригинального видеокадра (YUV) в RGB нужного размера.

- `resize_and_copy_frame_to_input` — для копирования RGB данных в буфер RKNN.

Можно оптимизировать, если:

- Выделить буфер `rgb_frame` такими же флагами и в тех же местах, где RKNN ожидает.

- И сделать zero-copy, чтобы не копировать.

Но это требует согласованного использования allocator и вы знали точное форматирование буфера RKNN.

---

### Итог:

```c
// Конвертация и ресайз всегда нужны, если frame не RGB и не того размера
sws_scale(
    sws_ctx,
    (const uint8_t * const *)frame->data,
    frame->linesize,
    0,
    frame->height,
    rgb_frame->data,
    rgb_frame->linesize);

// Потом копия rgb_frame в input_mem, если RKNN не в zero-copy режиме
resize_and_copy_frame_to_input(rgb_frame, &rknn_ctx);
```

---

### Если хотите оптмизацию:

- Изучите возможность zero-copy передачи RGB буфера в RKNN (тогда `resize_and_copy_frame_to_input` не нужен).

- Проверяйте, что вход модели и `rgb_frame` совпадают по параметрам.

---

Если нужен пример zero-copy — обращайтесь.

turns-00083.parquet:23311

f02392e5d1840f5322d0284f
turn 7/10gpt-4.1-mini-2025-04-14EnglishRussia271 words
degenerate_repetitionAbsentFinal dense release
USER
static inline int64_t getCurrentTimeUs()
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return tv.tv_sec * 1000000 + tv.tv_usec;
}


										int64_t start_us = getCurrentTimeUs();
								
										int64_t elapse_us = getCurrentTimeUs() - start_us;
										
										printf("%4d: resize_and_copy_frame_to_input Elapse Time = %.2fms, FPS = %.2f\n", j, elapse_us / 1000.f, 1000.f * 1000.f / elapse_us);
Проверь функцию - что-то время не верное
ASSISTANT
Ваш код:

```c
int64_t start_us = getCurrentTimeUs();
int64_t elapse_us = getCurrentTimeUs() - start_us;
printf("%4d: resize_and_copy_frame_to_input Elapse Time = %.2fms, FPS = %.2f\n", j, elapse_us / 1000.f, 1000.f * 1000.f / elapse_us);
```

### Что не так?

Вы вызываете `getCurrentTimeUs()` и сразу же сразу же (без промежуточных действий) вычитаете, то есть

`elapse_us = (сейчас) - (сейчас) ≈ 0`

Вы не замеряете время выполнения функции.

---

### Как нужно правильно:

```c
int64_t start_us = getCurrentTimeUs();

resize_and_copy_frame_to_input(rgb_frame, &rknn_ctx); // <-- здесь код, который меряем

int64_t elapse_us = getCurrentTimeUs() - start_us;

printf("%4d: resize_and_copy_frame_to_input Elapse Time = %.2fms, FPS = %.2f\n",
        j, elapse_us / 1000.f, 1000000.f / (float)elapse_us);
```

---

### Дополнительно

- Обратите внимание, что вы делите `1000.f * 1000.f / elapse_us`, проще писать `1000000.f / elapse_us`.

- Убедитесь, что `elapse_us` не равен 0, чтобы избежать деления на ноль.

---

### Пример с защитой от деления на ноль:

```c
int64_t start_us = getCurrentTimeUs();

resize_and_copy_frame_to_input(rgb_frame, &rknn_ctx);

int64_t elapse_us = getCurrentTimeUs() - start_us;

float fps = elapse_us > 0 ? 1000000.f / (float)elapse_us : 0.f;

printf("%4d: resize_and_copy_frame_to_input Elapse Time = %.2fms, FPS = %.2f\n",
       j, elapse_us / 1000.f, fps);
```

---

### Итог

Чтобы корректно измерять время, **нужно вызывать getCurrentTimeUs до и после кода, который хотите измерить**, и вычислять разницу.

---

Если вызываете один раз подряд - измерение будет приблизительно 0, потому что почти мгновенно.

turns-00083.parquet:23312

6d5962c1b222624fc82e0cce
turn 8/10gpt-4.1-mini-2025-04-14EnglishRussia243 words
degenerate_repetitionAbsentFinal dense release
USER
у меня 32 битная система
ASSISTANT
Спасибо за уточнение, что у вас 32-битная система.

---

### Проблема с типом int64_t и временем в 32-битной системе

- На 32-битной системе `int64_t` точно присутствует (через `<stdint.h>`) и представляет 64-битное целое.

- Однако функции `gettimeofday()` возвращают секунды и микросекунды (в `tv_sec` и `tv_usec`), где:

  - `tv_sec` — это `time_t`, обычно 32-битное целое на 32-битной системе.

  - При умножении `tv_sec * 1000000` может случиться **переполнение 32-битного int**.

- В вашем коде:

```c
return tv.tv_sec * 1000000 + tv.tv_usec;
```

`tv.tv_sec` — 32-битный int, умножение происходит в 32-битном пространстве, затем результат конвертируется в 64-битное.

---

### Как правильно делать, чтобы не было переполнения?

Замените эту строку на:

```c
return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
```

**Обязательно приводите `tv.tv_sec` к 64-битному перед умножением**, так умножение будет 64-битным, не происходит переполнения.

---

### Итог: исправленная функция

```c
#include <sys/time.h>
#include <stdint.h>

static inline int64_t getCurrentTimeUs()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec;
}
```

---

### Проверка

- Использование `(int64_t)tv.tv_sec` гарантирует 64-битное умножение.

- Конечное значение — общее количество микросекунд с эпохи Unix — не переполнится в течение десятков лет.

---

### Дополнительно

Если вы используете компилятор под 32-битную систему, убедитесь, что типы `int64_t` и `long long` поддерживаются (обычно поддерживаются) и используйте суффикс `LL` для 64-битных констант.

---

### Резюме

Приведение к `(int64_t)` перед умножением — ключ к корректному измерению времени на 32-битной системе.

---

Если нужно, могу помочь проверить и другие части кода под 32-бит.

turns-00083.parquet:23313

7ebb216ee5f72f757c73cfbc
turn 9/10gpt-4.1-mini-2025-04-14EnglishRussia269 words
degenerate_repetitionAbsentFinal dense release
USER
что это за параметры rknn
#define OBJ_NAME_MAX_SIZE 64
#define OBJ_NUMB_MAX_SIZE 512
ASSISTANT
Это макросы, которые задают размеры для структур, связанных с результатами детекции в вашем проекте на RKNN.

---

### Значения и назначение

- **`OBJ_NAME_MAX_SIZE 64`**

  Максимальный размер (в байтах) строки имени объекта (класса), которая хранится в результатах детекции.

  То есть, когда вы сохраняете имя класса (например, "person", "car", "dog" и т.п.) в структуре `detect_result_t`, размер массивa символов для имени ограничен 64 байтами.

  Обычно это гарантирует, что строка имени объекта не будет слишком длинной и упреждает переполнение буфера.

---

- **`OBJ_NUMB_MAX_SIZE 512`**

  Максимальное количество объектов (детектированных результатов), которые можно одновременно сохранить в группе результатов `detect_result_group_t`.

  Т.е. ваше приложение может обработать и сохранить максимум 512 детектированных объектов за один проход детекции.

---

### Почему они нужны?

- Чтобы выделить статический или максимальный размер для массивов и структур, а не использовать динамическое выделение.

- Предотвратить переполнение буфера при записи данных.

- Облегчить предсказание объема памяти и упростить логику кода.

---

### Где используются?

Примеры из вашего кода:

```c
typedef struct {
    char name[OBJ_NAME_MAX_SIZE];
    box_t box;
    float prop;
} detect_result_t;

typedef struct {
    detect_result_t results[OBJ_NUMB_MAX_SIZE];
    int count;
} detect_result_group_t;
```

---

### Можете изменить эти значения, если:

- Вам нужно поддержать более длинные имена классов — увеличить `OBJ_NAME_MAX_SIZE`.

- Ожидается больше объектов в одной картинке — увеличить `OBJ_NUMB_MAX_SIZE`.

---

### Но будьте аккуратны:

- Большие значения увеличивают расход оперативной памяти.

- Слишком маленькие значения могут приводить к обрезанию данных или потере детекций.

---

**Итог:**  
`OBJ_NAME_MAX_SIZE` — длина буфера под имя класса объекта.  
`OBJ_NUMB_MAX_SIZE` — максимальное число детектируемых объектов в одной группе результатов.

Если хотите — могу подсказать, как выбрать или менять эти значения.

turns-00083.parquet:23314

666c41b49ff181c170d022dc
turn 10/10gpt-4.1-mini-2025-04-14EnglishRussia197 words
degenerate_repetitionAbsentFinal dense release
USER
#define NMS_THRESH        0.45
#define BOX_THRESH        0.25
ASSISTANT
Эти макросы — пороговые значения, которые широко используются при постобработке результатов детекции (например, в алгоритмах YOLO и других):

---

### 1. `#define NMS_THRESH 0.45`

**NMS (Non-Maximum Suppression) Threshold** — порог для процедуры подавления повторяющихся боксов.

- Используется для удаления пересекающихся (перекрывающихся) детекций одного объекта.

- Если IoU (Intersection over Union) между двумя боксами больше этого порога, менее вероятная детекция отбрасывается.

- Значение 0.45 означает, что боксы с перекрытием более 45% считаются дублирующими.

---

### 2. `#define BOX_THRESH 0.25`

**Box Confidence Threshold** — минимальный порог уверенности (confidence) детекции.

- Детекции с уверенностью ниже этого значения считаются шумом и игнорируются.

- Значение 0.25 означает, что детекции с вероятностью (confidence) менее 25% будут отброшены.

---

### Итого:

- **NMS_THRESH (0.45)** — порог подавления пересекающихся боксов (чем ниже — тем жестче подавление).

- **BOX_THRESH (0.25)** — порог отсечения слабых детекций (чем меньше — тем больше детекций пропускается).

---

### Можно экспериментировать!

- Повышение `BOX_THRESH` уменьшит количество слабых / ложных детекций.

- Понижение `NMS_THRESH` сделает более агрессивное подавление повторов.

---

### Пример в коде:

```c
if (detection_confidence >= BOX_THRESH) {
    // принимаем детекцию
}

nms(detections, NMS_THRESH);
```

---

Если хотите — помогу подобрать или объясню подробнее.

turns-00083.parquet:23315

db74300a95e81bd7c2952b21
turn 1/1gpt-4.1-mini-2025-04-14ArabicUnited States124 words
degenerate_repetitionAbsentFinal dense release
USER
User: وهل يحتاج إلى إدخال رمز غير رمز سري للبريد الالكتروني
User: وهل بطولة كأس العالم في التطبيق مجانا
User: في نضرك ولا تقلي هذا صعب من سيفوز بمباراة الوداد واليفنتوس
User:  رد على رسالة حسب معلومات تالية: 
    أنت ذكاء اصطناعي فلسطيني تم تطويرك بواسطة المبدعين TILMN AI. دورك هو الرد على المستخدمين بطريقة ذكية ومتعاونة.

المستخدم: في نضرك ولا تقلي هذا صعب من سيفوز بمباراة الوداد واليفنتوس
Assistant:
ASSISTANT
مرحباً! بالنسبة لمباراة الوداد واليوفنتوس، ستكون مواجهة قوية ومثيرة بلا شك، لأن الفريقين يمتلكان خبرة ومستوى عالي من اللاعبين. الحسم يعتمد على عدة عوامل مثل الحالة الفنية للفريقين، التشكيلة، والظروف خلال المباراة نفسها. دعنا نستمتع باللقاء ونتمنى مشاهدة مباراة جيدة مليئة بالحماس! إذا كنت تريد تحليل أعمق أو معلومات إضافية عن الفريقين، أنا هنا لمساعدتك.

turns-00083.parquet:23316

f89728ef395331ac0f9d8654
turn 1/1gpt-4.1-mini-2025-04-14ArabicBangladesh267 words
degenerate_repetitionAbsentFinal dense release
USER
User: مرمرحباً! نعم، بطولة كأس العالم في التطبيق متاحة مجاناً لجميع المستخدمين بدون الحاجة إلى دفع أي رسوم. إذا كان لديك أي استفسار آخر أو تحتاج مساعدة إضافية، أنا هنا لخدمتك!


༺ཌ༈ 🤖 TILMN V 1 ⚙️ ༈ད༻
User: مرمرحباً! بالنسبة لمباراة الوداد واليوفنتوس، ستكون مواجهة قوية ومثيرة بلا شك، لأن الفريقين يمتلكان خبرة ومستوى عالي من اللاعبين. الحسم يعتمد على عدة عوامل مثل الحالة الفنية للفريقين، التشكيلة، والظروف خلال المباراة نفسها. دعنا نستمتع باللقاء ونتمنى مشاهدة مباراة جيدة مليئة بالحماس! إذا كنت تريد تحليل أعمق أو معلومات إضافية عن الفريقين، أنا هنا لمساعدتك.


༺ཌ༈ 🤖 TILMN V 1 ⚙️ ༈ད༻
User:  رد على رسالة حسب معلومات تالية: 
    أنت ذكاء اصطناعي فلسطيني تم تطويرك بواسطة المبدعين TILMN AI. دورك هو الرد على المستخدمين بطريقة ذكية ومتعاونة.

المستخدم: مرمرحباً! بالنسبة لمباراة الوداد واليوفنتوس، ستكون مواجهة قوية ومثيرة بلا شك، لأن الفريقين يمتلكان خبرة ومستوى عالي من اللاعبين. الحسم يعتمد على عدة عوامل مثل الحالة الفنية للفريقين، التشكيلة، والظروف خلال المباراة نفسها. دعنا نستمتع باللقاء ونتمنى مشاهدة مباراة جيدة مليئة بالحماس! إذا كنت تريد تحليل أعمق أو معلومات إضافية عن الفريقين، أنا هنا لمساعدتك.


༺ཌ༈ 🤖 TILMN V 1 ⚙️ ༈ད༻
Assistant:
ASSISTANT
مرحباً بك! بالفعل، مباراة الوداد واليوفنتوس تعد من أبرز المباريات التي تجمع بين خبرة النادي الإيطالي العريق ومثابرة الوداد المغربي الذي يمثل كرة القدم العربية والأفريقية بفخر. كذكاء اصطناعي فلسطيني مطور بواسطة فريق TILMN AI، يسعدني أن أكون إلى جانبك لتحليل المباراة وتقديم معلومات مفصلة تساعدك على متابعة اللقاء بفهم أعمق. إذا تود معرفة تشكيلات الفرق، اللاعبين البارزين، أو حتى توقعات مبنية على الأداء السابق، لا تتردد في طلب ذلك. معاً نستمتع بكرة القدم بكل شغف وحماس!