--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/**
+ * GStreamer Tensor_Filter, nntrainer Module
+ * Copyright (C) 2020 Jijoong Moon <jijoong.moon@samsung.com>
+ */
+/**
+ * @file tensor_filter_nntrainer.cc
+ * @date 09 Sept 2020
+ * @brief nntrainer inference module for tensor_filter gstreamer plugin
+ * @see http://github.com/nnsuite/nnstreamer
+ * @author Jijoong Moon <jijoong.moon@samsung.com>
+ * @bug No known bugs except for NYI items
+ *
+ * This is the per-NN-framework plugin (tensorflow-lite) for tensor_filter.
+ * Fill in "GstTensorFilterFramework" for tensor_filter.h/c
+ *
+ */
+
+#include <algorithm>
+#include <limits.h>
+#include <unistd.h>
+
+#include <nnstreamer_plugin_api.h>
+#include <nnstreamer_plugin_api_filter.h>
+
+#include <neuralnet.h>
+
+#define ml_loge g_critical
+
+/**
+ * @brief Macro for debug mode.
+ */
+#ifndef DBG
+#define DBG FALSE
+#endif
+
+#define NUM_DIM 4
+
+static const gchar *nntrainer_accl_support[] = {NULL};
+
+/**
+ * @brief Internal data structure for nntrainer
+ */
+typedef struct {
+ int rank;
+ std::vector<std::int64_t> dims;
+} nntrainer_tensor_info_s;
+
+class NNTrainer {
+public:
+ /**
+ * member functions.
+ */
+ NNTrainer(const char *model_config_);
+ ~NNTrainer();
+
+ int init(const GstTensorFilterProperties *prop);
+ int loadModel();
+ const char *getModelConfig();
+
+ int getInputTensorDim(GstTensorsInfo *info);
+ int getOutputTensorDim(GstTensorsInfo *info);
+ int run(const GstTensorMemory *input, GstTensorMemory *output);
+ void freeOutputTensor(void *data);
+ int validateTensor(const GstTensorsInfo *tensorInfo, bool is_input);
+
+private:
+ char *model_config;
+ nntrainer::NeuralNetwork *model;
+
+ GstTensorsInfo inputTensorMeta;
+ GstTensorsInfo outputTensorMeta;
+
+ std::vector<nntrainer_tensor_info_s> input_tensor_info;
+ std::map<void *, std::shared_ptr<nntrainer::Tensor>> outputTensorMap;
+};
+
+void init_filter_nntrainer(void) __attribute__((constructor));
+void fini_filter_nntrainer(void) __attribute__((destructor));
+
+NNTrainer::NNTrainer(const char *model_config_) {
+ g_assert(model_config_ != NULL);
+ model = nullptr;
+ model_config = g_strdup(model_config_);
+ gst_tensors_info_init(&inputTensorMeta);
+ gst_tensors_info_init(&outputTensorMeta);
+}
+
+NNTrainer::~NNTrainer() {
+ if (model != nullptr) {
+ model->finalize();
+ delete model;
+ }
+
+ gst_tensors_info_free(&inputTensorMeta);
+ gst_tensors_info_free(&outputTensorMeta);
+ g_free(model_config);
+}
+
+int NNTrainer::init(const GstTensorFilterProperties *prop) {
+ if (loadModel()) {
+ ml_loge("Failed to load model");
+ return -1;
+ }
+
+ if (validateTensor(&prop->input_meta, true)) {
+ ml_loge("Failed to validate input tensor");
+ return -2;
+ }
+
+ if (validateTensor(&prop->output_meta, false)) {
+ ml_loge("Failed to validate output tensor");
+ return -3;
+ }
+
+ try {
+ model->init();
+ model->readModel();
+ } catch (...) {
+ ml_loge("Failed to initialize model");
+ model->finalize();
+ return -1;
+ }
+
+ gst_tensors_info_copy(&inputTensorMeta, &prop->input_meta);
+ gst_tensors_info_copy(&outputTensorMeta, &prop->output_meta);
+
+ return 0;
+}
+
+const char *NNTrainer::getModelConfig() { return model_config; }
+
+int NNTrainer::validateTensor(const GstTensorsInfo *tensorInfo, bool is_input) {
+
+ nntrainer::TensorDim dim;
+ nntrainer_tensor_info_s info_s;
+ unsigned int order[3] = {1, 3, 2};
+
+ if (is_input)
+ dim = model->getInputDimension();
+ else
+ dim = model->getOutputDimension();
+
+ g_assert(tensorInfo->info[0].type == _NNS_FLOAT32);
+ info_s.rank = NUM_DIM;
+
+ for (unsigned int i = 0; i < NUM_DIM - 1; ++i) {
+ g_assert(tensorInfo->info[0].dimension[i] == dim.getDim()[order[i]]);
+ info_s.dims.push_back(dim.getDim()[order[i]]);
+ }
+
+ g_assert(tensorInfo->info[0].dimension[NUM_DIM - 1] == dim.getDim()[0]);
+ info_s.dims.push_back(dim.getDim()[0]);
+
+ if (is_input) {
+ input_tensor_info.push_back(info_s);
+ }
+
+ return 0;
+}
+
+int NNTrainer::loadModel() {
+#if (DBG)
+ gint64 start_time = g_get_real_time();
+#endif
+ gsize file_size;
+ gchar *content = nullptr;
+ GError *file_error = nullptr;
+
+ g_assert(model_config != nullptr);
+ if (!g_file_test(model_config, G_FILE_TEST_IS_REGULAR)) {
+ ml_loge("the file of model_config (%s) is not valid (not regular)\n",
+ model_config);
+ return -1;
+ }
+
+ if (!g_file_get_contents(model_config, &content, &file_size, &file_error)) {
+ ml_loge("Error reading model config!! - %s", file_error->message);
+ g_clear_error(&file_error);
+ return -2;
+ }
+
+ try {
+ model = new nntrainer::NeuralNetwork(false);
+ model->loadFromConfig(model_config);
+ } catch (...) {
+ ml_loge("Cannot load model from config\n");
+ model->finalize();
+ return -1;
+ }
+
+#if (DBG)
+ gint64 stop_time = g_get_real_time();
+ g_message("Model is loaded: %" G_GINT64_FORMAT, (stop_time - start_time));
+#endif
+ return 0;
+}
+
+int NNTrainer::getInputTensorDim(GstTensorsInfo *info) {
+ gst_tensors_info_copy(info, &inputTensorMeta);
+ return 0;
+}
+
+int NNTrainer::getOutputTensorDim(GstTensorsInfo *info) {
+ gst_tensors_info_copy(info, &outputTensorMeta);
+ return 0;
+}
+
+int NNTrainer::run(const GstTensorMemory *input, GstTensorMemory *output) {
+#if (DBG)
+ gint64 start_time = g_get_real_time();
+#endif
+ std::vector<std::shared_ptr<nntrainer::Tensor>> output_tensors;
+ std::shared_ptr<nntrainer::Tensor> out;
+
+ std::vector<std::int64_t> d = input_tensor_info[0].dims;
+ nntrainer::Tensor X =
+ nntrainer::Tensor(nntrainer::TensorDim(d[3], d[0], d[2], d[1]),
+ static_cast<float *>(input[0].data));
+
+ output_tensors.push_back(out);
+ std::shared_ptr<const nntrainer::Tensor> o;
+
+ o = model->inference(X);
+ if (o == nullptr) {
+ model->finalize();
+ return -1;
+ }
+
+ out = std::const_pointer_cast<nntrainer::Tensor>(o);
+
+ output[0].data = out->getData();
+
+ outputTensorMap.insert(std::make_pair(output[0].data, output_tensors[0]));
+
+#if (DBG)
+ gint64 stop_time = g_get_real_time();
+ g_message("Run() is finished: %" G_GINT64_FORMAT, (stop_time - start_time));
+#endif
+ return 0;
+}
+
+void NNTrainer::freeOutputTensor(void *data) {
+ if (data != nullptr) {
+ std::map<void *, std::shared_ptr<nntrainer::Tensor>>::iterator it =
+ outputTensorMap.find(data);
+ if (it != outputTensorMap.end()) {
+ outputTensorMap.erase(data);
+ }
+ }
+}
+
+static void nntrainer_close(const GstTensorFilterProperties *prop,
+ void **private_data) {
+ NNTrainer *nntrainer = static_cast<NNTrainer *>(*private_data);
+
+ if (!nntrainer)
+ return;
+ delete nntrainer;
+ *private_data = NULL;
+}
+
+static int nntrainer_loadModelFile(const GstTensorFilterProperties *prop,
+ void **private_data) {
+ NNTrainer *nntrainer;
+ const gchar *model_file;
+ if (prop->num_models != 1)
+ return -1;
+
+ nntrainer = static_cast<NNTrainer *>(*private_data);
+ model_file = prop->model_files[0];
+
+ if (nntrainer != NULL) {
+ if (g_strcmp0(model_file, nntrainer->getModelConfig()) == 0)
+ return 1; /* skipped */
+
+ nntrainer_close(prop, private_data);
+ }
+
+ nntrainer = new NNTrainer(model_file);
+ if (nntrainer == NULL) {
+ g_printerr("Failed to allocate memory for filter subplugin: nntrainer\n");
+ return -1;
+ }
+
+ if (nntrainer->init(prop) != 0) {
+ *private_data = NULL;
+ delete nntrainer;
+
+ g_printerr("failed to initailize the object: nntrainer\n");
+ return -2;
+ }
+
+ *private_data = nntrainer;
+ return 0;
+}
+
+static int nntrainer_open(const GstTensorFilterProperties *prop,
+ void **private_data) {
+ int status = nntrainer_loadModelFile(prop, private_data);
+ return status;
+}
+
+static int nntrainer_run(const GstTensorFilterProperties *prop,
+ void **private_data, const GstTensorMemory *input,
+ GstTensorMemory *output) {
+ NNTrainer *nntrainer = static_cast<NNTrainer *>(*private_data);
+ g_return_val_if_fail(nntrainer && input && output, -EINVAL);
+
+ return nntrainer->run(input, output);
+}
+
+static int nntrainer_getInputDim(const GstTensorFilterProperties *prop,
+ void **private_data, GstTensorsInfo *info) {
+ NNTrainer *nntrainer = static_cast<NNTrainer *>(*private_data);
+ g_return_val_if_fail(nntrainer && info, -EINVAL);
+ return nntrainer->getInputTensorDim(info);
+}
+
+static int nntrainer_getOutputDim(const GstTensorFilterProperties *prop,
+ void **private_data, GstTensorsInfo *info) {
+ NNTrainer *nntrainer = static_cast<NNTrainer *>(*private_data);
+ g_return_val_if_fail(nntrainer && info, -EINVAL);
+ return nntrainer->getOutputTensorDim(info);
+}
+
+static void nntrainer_destroyNotify(void **private_data, void *data) {
+ NNTrainer *nntrainer = static_cast<NNTrainer *>(*private_data);
+ std::cout << "nnstrainer_dest" << std::endl;
+ if (nntrainer) {
+ nntrainer->freeOutputTensor(data);
+ }
+}
+
+static int nntrainer_checkAvailability(accl_hw hw) {
+ if (g_strv_contains(nntrainer_accl_support, get_accl_hw_str(hw)))
+ return 0;
+
+ return -ENOENT;
+}
+
+static gchar filter_subplugin_nntrainer[] = "nntrainer";
+
+static GstTensorFilterFramework NNS_support_nntrainer = {
+ .version = GST_TENSOR_FILTER_FRAMEWORK_V0,
+ .open = nntrainer_open,
+ .close = nntrainer_close,
+};
+
+void init_filter_nntrainer(void) {
+ NNS_support_nntrainer.name = filter_subplugin_nntrainer;
+ NNS_support_nntrainer.allow_in_place = FALSE;
+ NNS_support_nntrainer.allocate_in_invoke = TRUE;
+ NNS_support_nntrainer.run_without_model = FALSE;
+ NNS_support_nntrainer.verify_model_path = FALSE;
+ NNS_support_nntrainer.invoke_NN = nntrainer_run;
+ NNS_support_nntrainer.getInputDimension = nntrainer_getInputDim;
+ NNS_support_nntrainer.getOutputDimension = nntrainer_getOutputDim;
+ NNS_support_nntrainer.destroyNotify = nntrainer_destroyNotify;
+ NNS_support_nntrainer.checkAvailability = nntrainer_checkAvailability;
+
+ nnstreamer_filter_probe(&NNS_support_nntrainer);
+}
+
+void fini_filter_nntrainer(void) {
+ nnstreamer_filter_exit(NNS_support_nntrainer.name);
+}
--- /dev/null
+#!/usr/bin/env python
+
+##
+# SPDX-License-Identifier: LGPL-2.1-only
+#
+# Copyright (C) 2018 Samsung Electronics
+#
+# @file gen24bBMP.py
+# @brief Generate 24bpp .bmp files for test cases
+# @author MyungJoo Ham <myungjoo.ham@samsung.com>
+
+from __future__ import print_function
+
+from struct import pack
+import random
+import sys
+
+##
+# @brief Convert given data to bytes
+# @param[in] data The data to be converted to bytes array
+# @return bytes converted from the data
+
+def convert_to_bytes(data):
+ """
+ Convert given data to bytes
+
+ @param data: The data to be converted to bytes
+ @rtype : bytes
+ @return : bytes converted from the data
+ """
+
+ if isinstance(data, bytes):
+ return data
+ else:
+ return pack("<B", data)
+
+##
+# @brief Save bitmap "data" to "filename"
+# @param[in] filename The filename to be saves as a .bmp file.
+# @param[in] data string of RGB (packed in 'BBB') or BGRx (packed in 'BBBB')
+# @param[in] colorspace "RGB" or "BGRx"
+# @param[in] width Width of the picture
+# @param[in] height Height of the picture
+def saveBMP(filename, data, colorspace, width, height):
+ size = len(data)
+ graphics = b''
+ # Default value of bytes per pixel value for RGB
+ bytes_per_px = 3
+
+ if colorspace == 'RGB':
+ assert(size == (width * height * bytes_per_px))
+ # BMP is stored bottom to top. Reverse the order
+ for h in range(height-1, -1, -1):
+ for w in range(0, width):
+ pos = 3 * (w + width * h)
+ graphics += convert_to_bytes(data[pos + 2])
+ graphics += convert_to_bytes(data[pos + 1])
+ graphics += convert_to_bytes(data[pos])
+ for x in range(0, (width * 3) % 4):
+ graphics += pack('<B', 0)
+ elif colorspace == 'BGRx':
+ bytes_per_px = 4
+ assert(size == (width * height * bytes_per_px))
+ # BMP is stored bottom to top. Reverse the order
+ for h in range(height-1, -1, -1):
+ for w in range(0, width):
+ pos = bytes_per_px * (w + width * h)
+ graphics += convert_to_bytes(data[pos])
+ graphics += convert_to_bytes(data[pos + 1])
+ graphics += convert_to_bytes(data[pos + 2])
+ for x in range(0, (width * 3) % 4):
+ graphics += pack('<B', 0)
+ elif colorspace == 'GRAY8':
+ bytes_per_px = 1
+ assert(size == (width * height * bytes_per_px))
+ # BMP is stored bottom to top. Reverse the order
+ for h in range(height-1, -1, -1):
+ for w in range(0, width):
+ pos = bytes_per_px * (w + width * h)
+ graphics += convert_to_bytes(data[pos])
+ for x in range(0, (width * 3) % 4):
+ graphics += pack('<B', 0)
+ else:
+ print('Unrecognized colorspace %', colorspace)
+ sys.exit(1)
+
+ # BMP file header
+ if colorspace == 'GRAY8':
+ header = pack('<HLHHL', 19778, (26 + width * height), 0, 0, 26)
+ header += pack('<LHHHH', 12, width, height, 1, 8)
+ else:
+ header = pack('<HLHHL', 19778, (26 + width * height * 3), 0, 0, 26)
+ header += pack('<LHHHH', 12, width, height, 1, 24)
+
+ with open(filename, 'wb') as file:
+ file.write(header)
+ file.write(graphics)
+
+
+##
+# @brief Write the generated data
+#
+def write(filename, data):
+ with open(filename, 'wb') as file:
+ file.write(data)
+
+
+##
+# @brief Generate Golden Test Case, a single videosrctest frame of 280x40xRGB
+# @return (string, string_size, expected_size)
+#
+# If string_size < expected_size, you do not need to check the results offset >= string_size.
+# string: binary string (b'\xff\x00....')
+def gen_RGB():
+ string = b''
+ string_size = 0
+ expected_size = 280 * 40 * 3
+ for i in range(0, 26):
+ # White
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 255, 255, 255)
+ # Yellow
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 255, 255, 0)
+ # Light Blue
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 0, 255, 255)
+ # Green
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 0, 255, 0)
+ # Purple
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 255, 0, 255)
+ # Red
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 255, 0, 0)
+ # Blue
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 0, 0, 255)
+ for i in range(26, 30):
+ # Blue
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 0, 0, 255)
+ # Black
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 0, 0, 0)
+ # Purple
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 255, 0, 255)
+ # Black
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 0, 0, 0)
+ # Light Blue
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 0, 255, 255)
+ # Black
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 0, 0, 0)
+ # White
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBB', 255, 255, 255)
+ for i in range(0, 46):
+ # Dark Blue
+ string_size = string_size + 46
+ string += pack('BBB', 0, 0, 128)
+ for i in range(46, 93):
+ # White
+ string_size = string_size + 47
+ string += pack('BBB', 255, 255, 255)
+ for i in range(93, 140):
+ # Gray Blue
+ string_size = string_size + 47
+ string += pack('BBB', 0, 128, 255)
+ for i in range(140, 186):
+ # Black
+ string_size = string_size + 46
+ string += pack('BBB', 0, 0, 0)
+ for i in range(186, 210):
+ # Dark Gray
+ string_size = string_size + 24
+ string += pack('BBB', 19, 19, 19)
+ # We do not check the reset pixels: they are randomly generated.
+ string_size = string_size * 3
+ return string, string_size, expected_size
+
+
+##
+# @brief Generate Golden Test Case, a single videosrctest frame of 280x40xBGRx
+# @return (string, string_size, expected_size)
+#
+def gen_BGRx():
+ string = b''
+ string_size = 0
+ expected_size = 280 * 40 * 4
+ for i in range(0, 26):
+ # White
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 255, 255, 255, 255)
+ # Yellow
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 0, 255, 255, 255)
+ # Light Blue
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 255, 255, 0, 255)
+ # Green
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 0, 255, 0, 255)
+ # Purple
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 255, 0, 255, 255)
+ # Red
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 0, 0, 255, 255)
+ # Blue
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 255, 0, 0, 255)
+ for i in range(26, 30):
+ # Blue
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 255, 0, 0, 255)
+ # Black
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 0, 0, 0, 255)
+ # Purple
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 255, 0, 255, 255)
+ # Black
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 0, 0, 0, 255)
+ # Light Blue
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 255, 255, 0, 255)
+ # Black
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 0, 0, 0, 255)
+ # White
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('BBBB', 255, 255, 255, 255)
+ for i in range(0, 46):
+ # Dark Blue
+ string_size = string_size + 46
+ string += pack('BBBB', 128, 0, 0, 255)
+ for i in range(46, 93):
+ # White
+ string_size = string_size + 47
+ string += pack('BBBB', 255, 255, 255, 255)
+ for i in range(93, 140):
+ # Gray Blue
+ string_size = string_size + 47
+ string += pack('BBBB', 255, 128, 0, 255)
+ for i in range(140, 186):
+ # Black
+ string_size = string_size + 46
+ string += pack('BBBB', 0, 0, 0, 255)
+ for i in range(186, 210):
+ # Dark Gray
+ string_size = string_size + 24
+ string += pack('BBBB', 19, 19, 19, 255)
+ # We do not check the reset pixels: they are randomly generated.
+ string_size = string_size * 4
+ return string, string_size, expected_size
+
+
+##
+# @brief Generate Golden Test Case, a single videosrctest frame of 280x40xGRAY8
+# @return (string, string_size, expected_size)
+#
+# If string_size < expected_size, you do not need to check the results offset >= string_size.
+# string: binary string (b'\xff\x00....')
+def gen_GRAY8():
+ string = b''
+ string_size = 0
+ expected_size = 280 * 40
+ for i in range(0, 26):
+ # 0xEB
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 235)
+ # 0xD2
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 210)
+ # 0xAA
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 170)
+ # 0x91
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 145)
+ # 0x6A
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 106)
+ # 0x51
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 81)
+ # 0x29
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 41)
+ for i in range(26, 30):
+ # 0x29
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 41)
+ # 0x10
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 16)
+ # 0x6A
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 106)
+ # 0x10
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 16)
+ # 0xAA
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 170)
+ # 0x10
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 16)
+ # 0xEB
+ string_size = string_size + 40
+ for j in range(0, 40):
+ string += pack('B', 235)
+ for i in range(0, 46):
+ # 0x10
+ string_size = string_size + 46
+ string += pack('B', 16)
+ for i in range(46, 93):
+ # 0xEB
+ string_size = string_size + 47
+ string += pack('B', 235)
+ for i in range(93, 140):
+ # 0x10
+ string_size = string_size + 47
+ string += pack('B', 16)
+ for i in range(140, 163):
+ # 0x00
+ string_size = string_size + 23
+ string += pack('B', 0)
+ for i in range(163, 186):
+ # 0x10
+ string_size = string_size + 23
+ string += pack('B', 16)
+ for i in range(186, 210):
+ # 0x20
+ string_size = string_size + 24
+ string += pack('B', 32)
+ # We do not check the reset pixels: they are randomly generated.
+ return string, string_size, expected_size
+
+##
+# @brief Generate Golden Test Case, a randomly generated BMP image
+# @return (string, string_size, expected_size)
+#
+def gen_BMP_random(color_type, width, height, filename_prefix):
+ string = b''
+ string_size = 0
+ size_per_pixel = 3
+ if color_type == 'BGRx':
+ size_per_pixel = 4
+ elif color_type == "GRAY8":
+ size_per_pixel = 1
+ expected_size = width * height * size_per_pixel
+ # The result has no stride for other/tensor types.
+
+ if color_type == 'BGRx':
+ for y in range(0, height):
+ for x in range(0, width):
+ pval = (random.randrange(256), random.randrange(256), random.randrange(256))
+ pixel = pack('BBBB', pval[2], pval[1], pval[0], 255)
+ string += pixel
+ string_size += 4
+ elif color_type == 'GRAY8':
+ for y in range(0, height):
+ for x in range(0, width):
+ pval = random.randrange(256)
+ pixel = pack('B', pval)
+ string += pixel
+ string_size += size_per_pixel
+ else:
+ # Assume RGB
+ for y in range(0, height):
+ for x in range(0, width):
+ pval = (random.randrange(256), random.randrange(256), random.randrange(256))
+ pixel = pack('BBB', pval[0], pval[1], pval[2])
+ string += pixel
+ string_size += 3
+
+ saveBMP(filename_prefix + '_' + color_type + '_' + str(width) + 'x' + str(height) + '.bmp',
+ string, color_type, width, height)
+ return string, string_size, expected_size
+
+
+##
+# @brief Generate a fixed BMP sequence for stream test
+# @return 0 if success. non-zero if failed.
+#
+# This gives "16x16", black, white, green, red, blue, wb-checker, rb-checker, gr-checker,
+# red-cross-on-white, blue-cross-on-black (4x4 x 16, left-top/right-bottom white/red/green).
+# "10 files" with 0 ~ 9 postfix in the filename
+def gen_BMP_stream(filename_prefix, golden_filename, num_sink):
+ string = [b'' for _ in range(10)]
+ size_x = 16
+ size_y = 16
+
+ for y in range(0, size_y):
+ for x in range(0, size_x):
+ # black. Frame 0
+ string[0] += pack('BBB', 0, 0, 0)
+ # white. Frame 1
+ string[1] += pack('BBB', 255, 255, 255)
+ # green, Frame 2
+ string[2] += pack('BBB', 0, 255, 0)
+ # red, Frame 3
+ string[3] += pack('BBB', 255, 0, 0)
+ # blue, Frame 4
+ string[4] += pack('BBB', 0, 0, 255)
+ # white-black checker, Frame 5
+ if (((x / 4) % 2) + ((y / 4) % 2)) == 1:
+ string[5] += pack('BBB', 0, 0, 0)
+ else:
+ string[5] += pack('BBB', 255, 255, 255)
+ # red-blue checker, Frame 6
+ if (((x / 4) % 2) + ((y / 4) % 2)) == 1:
+ string[6] += pack('BBB', 0, 0, 255)
+ else:
+ string[6] += pack('BBB', 255, 0, 0)
+ # green-red checker, Frame 7
+ if (((x / 4) % 2) + ((y / 4) % 2)) == 1:
+ string[7] += pack('BBB', 255, 0, 0)
+ else:
+ string[7] += pack('BBB', 0, 255, 0)
+ # red-cross-on-white, Frame 8
+ if x == y:
+ string[8] += pack('BBB', 255, 0, 0)
+ else:
+ string[8] += pack('BBB', 255, 255, 255)
+ # blue-cross-on-black, Frame 9
+ if x == y:
+ string[9] += pack('BBB', 0, 0, 255)
+ else:
+ string[9] += pack('BBB', 0, 0, 0)
+
+ with open(golden_filename, 'wb') as file:
+ for i in range(0, 10):
+ saveBMP(filename_prefix + '_' + str(i) + '.bmp', string[i], 'RGB', 16, 16)
+ for j in range(0, num_sink):
+ file.write(string[i])
+ return string