tfrun

An easy-to-use C++ wrapper over the stable C API of TensorFlow
Log | Files | Refs | README | LICENSE

commit 3fede9d004850b70fa3332b35538534eb188f3a1
parent 8646a39f76f0865c6b7fd91e3655793c6670a3b0
Author: Mohammad-Reza Nabipoor <m.nabipoor@yahoo.com>
Date:   Wed, 15 Apr 2020 06:10:16 +0430

Add new test case (using `MobileNetV2` network)

The path of `MobileNetV2` pre-trained model file (`.pb` file) is
determined by `MODEL_FILE` environment variable.

The test uses two input images (`dog.bmp` and `panda.bmp`); the
address of original files are in the `test/test-input-files.txt`.

Diffstat:
MCMakeLists.txt | 13+++++++++++++
Atest/bmpread.cpp | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/bmpread.hpp | 14++++++++++++++
Atest/dog.bmp | 0
Atest/model/mobilenet-v2-1.4-224.md | 45+++++++++++++++++++++++++++++++++++++++++++++
Atest/panda.bmp | 0
Atest/test-input-files.txt | 2++
Mtest/tfrun.test.cpp | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 241 insertions(+), 0 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -44,6 +44,9 @@ target_compile_options(tfrun add_executable(tfrun.test test/tfrun.test.cpp + + test/bmpread.cpp + test/bmpread.hpp ) target_link_libraries(tfrun.test PRIVATE @@ -54,6 +57,16 @@ set_property( PROPERTY CXX_STANDARD 14 ) +set(IMGFILES + ${CMAKE_SOURCE_DIR}/test/panda.bmp + ${CMAKE_SOURCE_DIR}/test/dog.bmp +) +add_custom_command( + TARGET tfrun.test + PRE_BUILD + COMMAND cp ${IMGFILES} ${CMAKE_CURRENT_BINARY_DIR} +) + install( TARGETS tfrun DESTINATION lib diff --git a/test/bmpread.cpp b/test/bmpread.cpp @@ -0,0 +1,113 @@ +// Copyright 2017 The TensorFlow Authors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// tensorflow/lite/examples/label_image/bitmap_helpers.cc + +#include "bmpread.hpp" + +#include <fstream> +#include <stdexcept> + +using namespace std::literals; + +namespace { + +std::vector<uint8_t> +bmpdecode(const uint8_t* input, + int row_size, + int width, + int height, + int channels, + bool top_down) +{ + std::vector<uint8_t> output(height * width * channels); + + for (int i = 0; i < height; i++) { + int src_pos; + int dst_pos; + + for (int j = 0; j < width; j++) { + if (!top_down) + src_pos = ((height - 1 - i) * row_size) + j * channels; + else + src_pos = i * row_size + j * channels; + dst_pos = (i * width + j) * channels; + + switch (channels) { + case 1: + output[dst_pos] = input[src_pos]; + break; + + case 3: + // BGR -> RGB + output[dst_pos] = input[src_pos + 2]; + output[dst_pos + 1] = input[src_pos + 1]; + output[dst_pos + 2] = input[src_pos]; + break; + + case 4: + // BGRA -> RGBA + output[dst_pos] = input[src_pos + 2]; + output[dst_pos + 1] = input[src_pos + 1]; + output[dst_pos + 2] = input[src_pos]; + output[dst_pos + 3] = input[src_pos + 3]; + break; + + default: + throw std::runtime_error{ "Unexpected number of channels: "s + + std::to_string(channels) }; + } + } + } + return output; +} + +} // namespace + +std::vector<uint8_t> +bmpread(const std::string& input_bmp_name, + int* width, + int* height, + int* channels) +{ + int begin, end; + + std::ifstream file(input_bmp_name, std::ios::in | std::ios::binary); + + if (!file) + throw std::runtime_error{ "input file "s + input_bmp_name + + " not found\n" }; + + begin = file.tellg(); + file.seekg(0, std::ios::end); + end = file.tellg(); + size_t len = end - begin; + + std::vector<uint8_t> img_bytes(len); + + file.seekg(0, std::ios::beg); + file.read(reinterpret_cast<char*>(img_bytes.data()), len); + + const int32_t header_size = + *(reinterpret_cast<const int32_t*>(img_bytes.data() + 10)); + + *width = *(reinterpret_cast<const int32_t*>(img_bytes.data() + 18)); + *height = *(reinterpret_cast<const int32_t*>(img_bytes.data() + 22)); + + const int32_t bpp = + *(reinterpret_cast<const int32_t*>(img_bytes.data() + 28)); + + *channels = bpp / 8; + + // there may be padding bytes when the width is not a multiple of 4 bytes + // 8 * channels == bits per pixel + const int row_size = (8 * *channels * *width + 31) / 32 * 4; + + // if height is negative, data layout is top down + // otherwise, it's bottom up + bool top_down = (*height < 0); + + // Decode image, allocating tensor once the image size is known + const uint8_t* bmp_pixels = &img_bytes[header_size]; + return bmpdecode( + bmp_pixels, row_size, *width, abs(*height), *channels, top_down); +} diff --git a/test/bmpread.hpp b/test/bmpread.hpp @@ -0,0 +1,14 @@ +// Copyright 2019-2020 Mohammad-Reza Nabipoor +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include <cstdint> +#include <string> +#include <vector> + +std::vector<uint8_t> +bmpread(const std::string& input_bmp_name, + int* width, + int* height, + int* channels); diff --git a/test/dog.bmp b/test/dog.bmp Binary files differ. diff --git a/test/model/mobilenet-v2-1.4-224.md b/test/model/mobilenet-v2-1.4-224.md @@ -0,0 +1,45 @@ +# `mobilenet-v2-1.4-224` + +**NOTE** This file is adapted from [here](https://github.com/opencv/open_model_zoo/blob/master/models/public/mobilenet-v2-1.4-224/mobilenet-v2-1.4-224.md). + +## Use Case and High-Level Description + +`mobilenet-v2-1.4-224` is one of MobileNets - small, low-latency, low-power +models parameterized to meet the resource constraints of a variety of use +cases. They can be built upon for classification, detection, embeddings and +segmentation similar to how other popular large scale models are used. +For details, see the [paper](https://arxiv.org/abs/1704.04861). + +## Example + +## Specification + +| Metric | Value | +|---------------------------------|-------------------------------------------| +| Type | Classification | +| GFlops | 1.183 | +| MParams | 6.087 | + +## Performance + +## Input + +Image, name: `input` , shape: [1x224x224x3], format: [BxHxWxC], where: + + - B - batch size + - H - image height + - W - image width + - C - number of channels + + Expected color order: RGB. + Mean values: [127.5, 127.5, 127.5], scale factor for each channel: 127.5 + +## Output + +Probabilities for all dataset classes (0 class is background). Probabilities +are represented in logits format. Name: `MobilenetV1/Predictions/Reshape_1`. + +## Legal Information + +The original model is distributed under the +[Apache License, Version 2.0](https://raw.githubusercontent.com/tensorflow/models/master/LICENSE). diff --git a/test/panda.bmp b/test/panda.bmp Binary files differ. diff --git a/test/test-input-files.txt b/test/test-input-files.txt @@ -0,0 +1,2 @@ +https://en.wikipedia.org/wiki/File:Giant_Panda_in_Beijing_Zoo_1.JPG +https://commons.wikimedia.org/wiki/File:Beagle_001.jpg diff --git a/test/tfrun.test.cpp b/test/tfrun.test.cpp @@ -9,6 +9,8 @@ #include <array> #include <cassert> +#include "bmpread.hpp" + std::vector<float> image(const std::vector<uint8_t>& in) { @@ -25,3 +27,55 @@ TEST_CASE("default ctor") REQUIRE_THROWS(net.run(nullptr, 0, nullptr, 0)); } + +TEST_CASE("MobileNet v2") +{ + const char* mpath = getenv("MODEL_FILE"); + + REQUIRE(mpath != nullptr); + + tfrun::siso net{ mpath, "input", "MobilenetV2/Predictions/Reshape_1" }; + enum + { + ndims = 4, + nclasses = 1000 + 1 /*background*/, + nbatch = 1, + }; + std::array<int64_t, ndims> dims{ nbatch, 224, 224, 3 }; + int width; + int height; + int chan; + std::vector<float> img; + + //--- beagle + + img = image(bmpread("dog.bmp", &width, &height, &chan)); + assert(img.size() == nbatch * 224 * 224 * 3); + + auto out = + net.run(dims.data(), ndims, img.data(), img.size() * sizeof(img[0])); + auto it = std::max_element(out.data.begin(), out.data.end()); + auto classIdx = it - out.data.begin(); + + REQUIRE(out.dims == std::vector<int64_t>{ + nbatch, + nclasses, + }); + REQUIRE(classIdx == 1 + 162 /*beagle*/); + + //--- panda + + img = image(bmpread("panda.bmp", &width, &height, &chan)); + assert(img.size() == nbatch * 224 * 224 * 3); + + out = net.run(dims.data(), ndims, img.data(), img.size() * sizeof(img[0])); + it = std::max_element(out.data.begin(), out.data.end()); + classIdx = it - out.data.begin(); + + REQUIRE(out.dims == std::vector<int64_t>{ + nbatch, + nclasses, + }); + REQUIRE(classIdx == 1 + + 388 /*giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca*/); +}