YOLOX.cpp 18.2 KB
Newer Older
1
2
#include <Filesystem.h>
#include <SimpleLog.h>
yaoht's avatar
yaoht committed
3
4
#include <YOLOX.h>
#include <migraphx/gpu/target.hpp>
5
#include <migraphx/onnx.hpp>
yaoht's avatar
yaoht committed
6
7
#include <migraphx/quantization.hpp>

8
namespace migraphxSamples {
yaoht's avatar
yaoht committed
9

10
DetectorYOLOX::DetectorYOLOX() {}
yaoht's avatar
yaoht committed
11

12
DetectorYOLOX::~DetectorYOLOX() { configurationFile.release(); }
yaoht's avatar
yaoht committed
13

14
15
ErrorCode DetectorYOLOX::Initialize(
    InitializationParameterOfDetector initializationParameterOfDetector) {
yaoht's avatar
yaoht committed
16
    // 读取配置文件
17
18
19
    std::string configFilePath =
        initializationParameterOfDetector.configFilePath;
    if (!Exists(configFilePath)) {
yaoht's avatar
yaoht committed
20
21
22
        LOG_ERROR(stdout, "no configuration file!\n");
        return CONFIG_FILE_NOT_EXIST;
    }
23
24
25
    if (!configurationFile.open(configFilePath, cv::FileStorage::READ)) {
        LOG_ERROR(stdout, "fail to open configuration file\n");
        return FAIL_TO_OPEN_CONFIG_FILE;
yaoht's avatar
yaoht committed
26
27
    }
    LOG_INFO(stdout, "succeed to open configuration file\n");
28

yaoht's avatar
yaoht committed
29
30
    // 获取配置文件参数
    cv::FileNode netNode = configurationFile["DetectorYOLOX"];
31
32
    modelPath = (std::string)netNode["ModelPathStatic"];
    std::string pathOfClassNameFile = (std::string)netNode["ClassNameFile"];
yaoht's avatar
yaoht committed
33
34
35
    yoloxParameter.confidenceThreshold = (float)netNode["ConfidenceThreshold"];
    yoloxParameter.nmsThreshold = (float)netNode["NMSThreshold"];
    yoloxParameter.objectThreshold = (float)netNode["ObjectThreshold"];
36
37
    yoloxParameter.numberOfClasses = (int)netNode["NumberOfClasses"];
    useFP16 = (bool)(int)netNode["UseFP16"];
yaoht's avatar
yaoht committed
38

39
40
41
42
    // 加载模型
    if (!Exists(modelPath)) {
        LOG_ERROR(stdout, "%s not exist!\n", modelPath.c_str());
        return MODEL_NOT_EXIST;
yaoht's avatar
yaoht committed
43
    }
44
45
46
47
48
49
50
51
52
    net = migraphx::parse_onnx(modelPath);
    LOG_INFO(stdout, "succeed to load model: %s\n",
             GetFileName(modelPath).c_str());

    // 获取模型输入/输出节点信息
    std::cout << "inputs:" << std::endl;
    std::unordered_map<std::string, migraphx::shape> inputs = net.get_inputs();
    for (auto i : inputs) {
        std::cout << i.first << ":" << i.second << std::endl;
yaoht's avatar
yaoht committed
53
    }
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    std::cout << "outputs:" << std::endl;
    std::unordered_map<std::string, migraphx::shape> outputs =
        net.get_outputs();
    for (auto i : outputs) {
        std::cout << i.first << ":" << i.second << std::endl;
    }
    inputName = inputs.begin()->first;
    inputShape = inputs.begin()->second;
    int N = inputShape.lens()[0];
    int C = inputShape.lens()[1];
    int H = inputShape.lens()[2];
    int W = inputShape.lens()[3];
    inputSize = cv::Size(W, H);

    // log
    LOG_INFO(stdout, "InputSize:%dx%d\n", inputSize.width, inputSize.height);

    LOG_INFO(stdout, "InputName:%s\n", inputName.c_str());
    LOG_INFO(stdout, "ConfidenceThreshold:%f\n",
             yoloxParameter.confidenceThreshold);
    LOG_INFO(stdout, "NMSThreshold:%f\n", yoloxParameter.nmsThreshold);
    LOG_INFO(stdout, "objectThreshold:%f\n", yoloxParameter.objectThreshold);
    LOG_INFO(stdout, "NumberOfClasses:%d\n", yoloxParameter.numberOfClasses);
yaoht's avatar
yaoht committed
77
78
79
80

    // 设置模型为GPU模式
    migraphx::target gpuTarget = migraphx::gpu::target{};

81
82
    // 量化
    if (useFP16) {
yaoht's avatar
yaoht committed
83
84
85
86
87
        migraphx::quantize_fp16(net);
    }

    // 编译模型
    migraphx::compile_options options;
88
89
90
91
92
    options.device_id = 0;
    options.offload_copy = true;
    net.compile(gpuTarget, options);
    LOG_INFO(stdout, "succeed to compile model: %s\n",
             GetFileName(modelPath).c_str());
yaoht's avatar
yaoht committed
93
94
95

    // warm up
    std::unordered_map<std::string, migraphx::argument> inputData;
96
    inputData[inputName] = migraphx::argument{inputShape};
yaoht's avatar
yaoht committed
97
98
99
    net.eval(inputData);

    // 读取类别名
100
    if (!pathOfClassNameFile.empty()) {
yaoht's avatar
yaoht committed
101
102
        std::ifstream classNameFile(pathOfClassNameFile);
        std::string line;
103
        while (getline(classNameFile, line)) {
yaoht's avatar
yaoht committed
104
105
            classNames.push_back(line);
        }
106
    } else {
yaoht's avatar
yaoht committed
107
108
109
110
111
112
        classNames.resize(yoloxParameter.numberOfClasses);
    }

    return SUCCESS;
}

113
114
115
116
117
118
119
120
void DetectorYOLOX::generate_grids_and_stride(
    std::vector<int> &strides, std::vector<GridAndStride> &grid_strides,
    cv::Size inputSize) {
    for (auto stride : strides) {
        int num_grid_y = inputSize.height / stride;
        int num_grid_x = inputSize.width / stride;
        for (int g1 = 0; g1 < num_grid_y; g1++) {
            for (int g0 = 0; g0 < num_grid_x; g0++) {
yaoht's avatar
yaoht committed
121
122
123
124
125
126
                grid_strides.push_back((GridAndStride){g0, g1, stride});
            }
        }
    }
}

127
128
129
void DetectorYOLOX::generate_yolox_proposals(
    std::vector<GridAndStride> grid_strides, float *feat_blob,
    float prob_threshold, std::vector<Object> &objects) {
yaoht's avatar
yaoht committed
130
131

    const int num_anchors = grid_strides.size();
132
133
    float max_box_objectness = 0;
    for (int anchor_idx = 0; anchor_idx < num_anchors; anchor_idx++) {
yaoht's avatar
yaoht committed
134
135
136
137
138
139
140
        const int grid0 = grid_strides[anchor_idx].grid0;
        const int grid1 = grid_strides[anchor_idx].grid1;
        const int stride = grid_strides[anchor_idx].stride;

        const int basic_pos = anchor_idx * (yoloxParameter.numberOfClasses + 5);

        // yolox/models/yolo_head.py decode logic
141
142
143
144
        float x_center = (feat_blob[basic_pos + 0] + grid0) * stride;
        float y_center = (feat_blob[basic_pos + 1] + grid1) * stride;
        float w = exp(feat_blob[basic_pos + 2]) * stride;
        float h = exp(feat_blob[basic_pos + 3]) * stride;
yaoht's avatar
yaoht committed
145
146
147
        float x0 = x_center - w * 0.5f;
        float y0 = y_center - h * 0.5f;

148
149
150
151
        float box_objectness = feat_blob[basic_pos + 4];
        max_box_objectness = box_objectness > max_box_objectness
                                 ? box_objectness
                                 : max_box_objectness;
yaoht's avatar
yaoht committed
152
153
        float max_box_cls_score = 0;
        int max_score_class_idx = 0;
154
155
        for (int class_idx = 0; class_idx < yoloxParameter.numberOfClasses;
             class_idx++) {
yaoht's avatar
yaoht committed
156
            float box_cls_score = feat_blob[basic_pos + 5 + class_idx];
157
            if (box_cls_score > max_box_cls_score) {
yaoht's avatar
yaoht committed
158
159
160
161
162
163
                max_box_cls_score = box_cls_score;
                max_score_class_idx = class_idx;
            }

        } // class loop
        float box_prob = box_objectness * max_box_cls_score;
164
165
166
167
168
169
170
171
172
173
        if (box_objectness > yoloxParameter.objectThreshold &&
            box_prob > yoloxParameter.confidenceThreshold) {
            Object obj;
            obj.rect.x = x0;
            obj.rect.y = y0;
            obj.rect.width = w;
            obj.rect.height = h;
            obj.label = max_score_class_idx;
            obj.prob = box_prob;
            objects.push_back(obj);
yaoht's avatar
yaoht committed
174
175
176
177
        }
    } // point anchor loop
}

178
179
void DetectorYOLOX::qsort_descent_inplace(std::vector<Object> &faceobjects,
                                          int left, int right) {
yaoht's avatar
yaoht committed
180
181
182
183
    int i = left;
    int j = right;
    float p = faceobjects[(left + right) / 2].prob;

184
    while (i <= j) {
yaoht's avatar
yaoht committed
185
186
187
188
189
190
        while (faceobjects[i].prob > p)
            i++;

        while (faceobjects[j].prob < p)
            j--;

191
        if (i <= j) {
yaoht's avatar
yaoht committed
192
193
194
195
196
197
198
199
            // swap
            std::swap(faceobjects[i], faceobjects[j]);

            i++;
            j--;
        }
    }

200
#pragma omp parallel sections
yaoht's avatar
yaoht committed
201
    {
202
#pragma omp section
yaoht's avatar
yaoht committed
203
        {
204
205
            if (left < j)
                qsort_descent_inplace(faceobjects, left, j);
yaoht's avatar
yaoht committed
206
        }
207
#pragma omp section
yaoht's avatar
yaoht committed
208
        {
209
210
            if (i < right)
                qsort_descent_inplace(faceobjects, i, right);
yaoht's avatar
yaoht committed
211
212
213
214
        }
    }
}

215
void DetectorYOLOX::qsort_descent_inplace(std::vector<Object> &objects) {
yaoht's avatar
yaoht committed
216
217
218
219
220
221
    if (objects.empty())
        return;

    qsort_descent_inplace(objects, 0, objects.size() - 1);
}

222
223
inline float DetectorYOLOX::intersection_area(const Object &a,
                                              const Object &b) {
yaoht's avatar
yaoht committed
224
225
226
227
    cv::Rect_<float> inter = a.rect & b.rect;
    return inter.area();
}

228
229
230
void DetectorYOLOX::nms_sorted_bboxes(const std::vector<Object> &faceobjects,
                                      std::vector<int> &picked,
                                      float nms_threshold) {
yaoht's avatar
yaoht committed
231
232
233
234
235
    picked.clear();

    const int n = faceobjects.size();

    std::vector<float> areas(n);
236
    for (int i = 0; i < n; i++) {
yaoht's avatar
yaoht committed
237
238
239
        areas[i] = faceobjects[i].rect.area();
    }

240
241
    for (int i = 0; i < n; i++) {
        const Object &a = faceobjects[i];
yaoht's avatar
yaoht committed
242
243

        int keep = 1;
244
245
        for (int j = 0; j < (int)picked.size(); j++) {
            const Object &b = faceobjects[picked[j]];
yaoht's avatar
yaoht committed
246
247
248
249
250
251
252
253
254
255
256
257
258
259

            // intersection over union
            float inter_area = intersection_area(a, b);
            float union_area = areas[i] + areas[picked[j]] - inter_area;
            // float IoU = inter_area / union_area
            if (inter_area / union_area > nms_threshold)
                keep = 0;
        }

        if (keep)
            picked.push_back(i);
    }
}

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
void DetectorYOLOX::decode_outputs(float *prob, std::vector<Object> &objects,
                                   float scalew, float scaleh, const int img_w,
                                   const int img_h, cv::Size inputSize) {
    std::vector<Object> proposals;
    std::vector<int> strides = {8, 16, 32};
    std::vector<GridAndStride> grid_strides;
    generate_grids_and_stride(strides, grid_strides, inputSize);
    generate_yolox_proposals(grid_strides, prob,
                             yoloxParameter.confidenceThreshold, proposals);
    std::cout << "num of boxes before nms: " << proposals.size() << std::endl;

    qsort_descent_inplace(proposals);

    std::vector<int> picked;
    nms_sorted_bboxes(proposals, picked, yoloxParameter.nmsThreshold);

    int count = picked.size();

    std::cout << "num of boxes: " << count << std::endl;

    objects.resize(count);
    for (int i = 0; i < count; i++) {
        objects[i] = proposals[picked[i]];

        // adjust offset to original unpadded
        float x0 = (objects[i].rect.x) / scalew;
        float y0 = (objects[i].rect.y) / scaleh;
        float x1 = (objects[i].rect.x + objects[i].rect.width) / scalew;
        float y1 = (objects[i].rect.y + objects[i].rect.height) / scaleh;

        // clip
        x0 = std::max(std::min(x0, (float)(img_w - 1)), 0.f);
        y0 = std::max(std::min(y0, (float)(img_h - 1)), 0.f);
        x1 = std::max(std::min(x1, (float)(img_w - 1)), 0.f);
        y1 = std::max(std::min(y1, (float)(img_h - 1)), 0.f);

        objects[i].rect.x = x0;
        objects[i].rect.y = y0;
        objects[i].rect.width = x1 - x0;
        objects[i].rect.height = y1 - y0;
    }
yaoht's avatar
yaoht committed
301
302
}

303
304
void meshgrid(const cv::Range &x_range, const cv::Range &y_range, cv::Mat &xv,
              cv::Mat &yv) {
yaoht's avatar
yaoht committed
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
    // 初始化矩阵大小
    int rows = y_range.end - y_range.start + 1;
    int cols = x_range.end - x_range.start + 1;

    // 创建 xv 和 yv 矩阵
    xv = cv::Mat(rows, cols, CV_32F);
    yv = cv::Mat(rows, cols, CV_32F);

    // 逐行逐列赋值
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            xv.at<float>(i, j) = static_cast<float>(j + x_range.start);
            yv.at<float>(i, j) = static_cast<float>(i + y_range.start);
        }
    }
}

cv::Mat demo_postprocess(cv::Mat outputs, cv::Size img_size, bool p6 = false) {
    std::vector<cv::Mat> grids;
    std::vector<cv::Mat> expanded_strides;
325
326
    std::vector<int> strides =
        p6 ? std::vector<int>{8, 16, 32, 64} : std::vector<int>{8, 16, 32};
yaoht's avatar
yaoht committed
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343

    std::vector<int> hsizes, wsizes;
    for (int stride : strides) {
        hsizes.push_back(img_size.height / stride);
        wsizes.push_back(img_size.width / stride);
    }

    for (size_t i = 0; i < strides.size(); ++i) {
        cv::Mat xv, yv;
        meshgrid(cv::Range(0, wsizes[i]), cv::Range(0, hsizes[i]), xv, yv);
        cv::Mat grid = cv::Mat::zeros(hsizes[i] * wsizes[i], 2, CV_32F);
        for (int j = 0; j < hsizes[i] * wsizes[i]; ++j) {
            grid.at<float>(j, 0) = xv.at<float>(j);
            grid.at<float>(j, 1) = yv.at<float>(j);
        }
        grids.push_back(grid);

344
345
        cv::Mat expanded_stride =
            cv::Mat::ones(hsizes[i] * wsizes[i], 1, CV_32F) * strides[i];
yaoht's avatar
yaoht committed
346
347
348
349
350
351
352
353
354
355
        expanded_strides.push_back(expanded_stride);
    }

    cv::Mat grids_concatenated, expanded_strides_concatenated;
    cv::vconcat(grids, grids_concatenated);
    cv::vconcat(expanded_strides, expanded_strides_concatenated);

    cv::Mat outputs_clone = outputs.clone();

    for (int i = 0; i < outputs_clone.rows; ++i) {
356
357
358
359
360
361
362
363
364
365
366
367
        outputs_clone.at<float>(i, 0) =
            (outputs.at<float>(i, 0) + grids_concatenated.at<float>(i, 0)) *
            expanded_strides_concatenated.at<float>(i, 0);
        outputs_clone.at<float>(i, 1) =
            (outputs.at<float>(i, 1) + grids_concatenated.at<float>(i, 1)) *
            expanded_strides_concatenated.at<float>(i, 0);
        outputs_clone.at<float>(i, 2) =
            exp(outputs.at<float>(i, 2)) *
            expanded_strides_concatenated.at<float>(i, 0);
        outputs_clone.at<float>(i, 3) =
            exp(outputs.at<float>(i, 3)) *
            expanded_strides_concatenated.at<float>(i, 0);
yaoht's avatar
yaoht committed
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
    }

    return outputs_clone;
}

std::vector<int> nms(cv::Mat boxes, cv::Mat scores, float nms_thr) {
    std::vector<int> keep;
    std::vector<float> areas(boxes.rows);

    // 计算所有框的面积
    for (int i = 0; i < boxes.rows; ++i) {
        float x1 = boxes.at<float>(i, 0);
        float y1 = boxes.at<float>(i, 1);
        float x2 = boxes.at<float>(i, 2);
        float y2 = boxes.at<float>(i, 3);

        areas[i] = (x2 - x1 + 1) * (y2 - y1 + 1);
    }

    // 根据分数排序的索引
    std::vector<int> order(scores.rows);
    std::iota(order.begin(), order.end(), 0);
390
391
392
    std::sort(order.begin(), order.end(), [&scores](int i, int j) {
        return scores.at<float>(i) > scores.at<float>(j);
    });
yaoht's avatar
yaoht committed
393
394
395
396
397
398

    // 执行 NMS
    while (!order.empty()) {
        int i = order[0];
        keep.push_back(i);

399
400
401
402
403
404
405
406
        float xx1 =
            std::max(boxes.at<float>(i, 0), boxes.at<float>(order[1], 0));
        float yy1 =
            std::max(boxes.at<float>(i, 1), boxes.at<float>(order[1], 1));
        float xx2 =
            std::min(boxes.at<float>(i, 2), boxes.at<float>(order[1], 2));
        float yy2 =
            std::min(boxes.at<float>(i, 3), boxes.at<float>(order[1], 3));
yaoht's avatar
yaoht committed
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425

        float w = std::max(0.0f, xx2 - xx1 + 1);
        float h = std::max(0.0f, yy2 - yy1 + 1);
        float inter = w * h;
        float ovr = inter / (areas[i] + areas[order[1]] - inter);

        std::vector<int> inds;
        for (size_t j = 1; j < order.size(); ++j) {
            if (ovr <= nms_thr) {
                inds.push_back(order[j]);
            }
        }

        order = inds;
    }

    return keep;
}

426
427
cv::Mat multiclass_nms_class_agnostic(cv::Mat boxes, cv::Mat scores,
                                      float nms_thr, float score_thr) {
yaoht's avatar
yaoht committed
428
429
430
431
432
    // 获取每个框的最高分数的索引和分数
    cv::Mat cls_inds;
    cv::Mat cls_scores = cv::Mat::zeros(scores.rows, 1, CV_32F);
    for (int i = 0; i < scores.rows; ++i) {
        int max_idx;
433
434
        // cv::minMaxIdx(scores.row(i), nullptr, &cls_scores.at<float>(i),
        // nullptr, &max_idx);
yaoht's avatar
yaoht committed
435
436
437
438
439
440
441
442
443
444
        double cls_score;
        cv::minMaxIdx(scores.row(i), nullptr, &cls_score, nullptr, &max_idx);
        cls_scores.at<float>(i) = static_cast<float>(cls_score);

        cls_inds.push_back(max_idx);
    }

    // 过滤掉低于阈值的分数
    cv::Mat valid_score_mask = cls_scores > score_thr;
    if (cv::countNonZero(valid_score_mask) == 0) {
445
        return cv::Mat(); // 如果没有有效的分数,返回空矩阵
yaoht's avatar
yaoht committed
446
447
448
449
    }

    // 保留有效分数对应的框和类别索引
    cv::Mat valid_scores = cls_scores(valid_score_mask);
450
451
    cv::Mat valid_boxes =
        boxes.rowRange(0, boxes.rows).clone(); // 复制框数据以便后续修改
yaoht's avatar
yaoht committed
452
453
454
455
456
    cv::Mat valid_cls_inds = cls_inds(valid_score_mask);

    // 应用 NMS 算法
    std::vector<int> keep = nms(valid_boxes, valid_scores, nms_thr);
    if (keep.empty()) {
457
        return cv::Mat(); // 如果没有保留的框,返回空矩阵
yaoht's avatar
yaoht committed
458
459
460
461
462
463
464
465
466
467
468
469
470
471
    }

    // 按行组合保留的框、分数和类别索引
    cv::Mat dets(keep.size(), 6, CV_32F);
    for (size_t i = 0; i < keep.size(); ++i) {
        int idx = keep[i];
        valid_boxes.row(idx).copyTo(dets.row(i).colRange(0, 4));
        dets.at<float>(i, 4) = valid_scores.at<float>(idx);
        dets.at<float>(i, 5) = valid_cls_inds.at<float>(idx);
    }

    return dets;
}

472
473
474
475
476
ErrorCode
DetectorYOLOX::Detect(const cv::Mat &srcImage,
                      const std::vector<std::size_t> &relInputShape,
                      std::vector<ResultOfDetection> &resultsOfDetection) {
    if (srcImage.empty() || srcImage.type() != CV_8UC3) {
yaoht's avatar
yaoht committed
477
478
479
480
481
482
483
        LOG_ERROR(stdout, "image error!\n");
        return IMAGE_ERROR;
    }

    // 数据预处理并转换为NCHW格式
    inputSize = cv::Size(relInputShape[3], relInputShape[2]);
    cv::Mat inputBlob;
484
485
486
487
    cv::dnn::blobFromImage(srcImage, inputBlob, 1, inputSize,
                           cv::Scalar(0, 0, 0), false, false);
    float ratio = std::min(inputSize.width / srcImage.rows,
                           inputSize.height / srcImage.cols);
yaoht's avatar
yaoht committed
488
489
    // 创建输入数据
    migraphx::parameter_map inputData;
490
491
492

    inputData[inputName] =
        migraphx::argument{inputShape, (float *)inputBlob.data};
yaoht's avatar
yaoht committed
493
494
495
496
497
498

    // 推理
    std::vector<migraphx::argument> inferenceResults = net.eval(inputData);

    // 获取推理结果
    std::vector<cv::Mat> outs;
499
    migraphx::argument result = inferenceResults[0];
yaoht's avatar
yaoht committed
500
501
502

    // 转换为cv::Mat
    migraphx::shape outputShape = result.get_shape();
503
504
505
506
    int shape[] = {outputShape.lens()[0], outputShape.lens()[1],
                   outputShape.lens()[2]};
    cv::Mat out(3, shape, CV_32F);
    memcpy(out.data, result.data(), sizeof(float) * outputShape.elements());
yaoht's avatar
yaoht committed
507
508
    outs.push_back(out);

509
    // 获取先验框的个数
yaoht's avatar
yaoht committed
510
511
    int numProposal = outs[0].size[1];
    int numOut = outs[0].size[2];
512
513

    // 变换输出的维度
yaoht's avatar
yaoht committed
514
    outs[0] = outs[0].reshape(0, numProposal);
515
    float *prob = (float *)outs[0].data;
yaoht's avatar
yaoht committed
516
517

    std::vector<Object> objects;
518
519
520
521
    float scalew = inputSize.width / (srcImage.cols * 1.0);
    float scaleh = inputSize.height / (srcImage.rows * 1.0);
    decode_outputs(prob, objects, scalew, scaleh, srcImage.cols, srcImage.rows,
                   inputSize);
yaoht's avatar
yaoht committed
522

523
    for (size_t i = 0; i < objects.size(); ++i) {
yaoht's avatar
yaoht committed
524
        ResultOfDetection result;
525
526
527
528
        result.boundingBox = objects[i].rect;
        result.confidence = objects[i].prob; // confidence
        result.classID = objects[i].label;   // label
        result.className = classNames[objects[i].label];
yaoht's avatar
yaoht committed
529
530
531
532
533
534
        resultsOfDetection.push_back(result);
    }

    return SUCCESS;
}

535
} // namespace migraphxSamples