为 TensorFlow Lite C++ 编写 read_jpeg 和 decode_jpeg 函数

Posted

技术标签:

【中文标题】为 TensorFlow Lite C++ 编写 read_jpeg 和 decode_jpeg 函数【英文标题】:Writing read_jpeg and decode_jpeg functions for TensorFlow Lite C++ 【发布时间】:2021-02-09 18:07:46 【问题描述】:

TensorFlow Lite 在其存储库here 中有一个很好的 C++ 图像分类示例。 但是,我正在使用 .jpeg,此示例仅限于使用 bitmap_helpers.cc 解码 .bmp 图像。

我正在尝试创建自己的 jpeg 解码器,但我并不精通图像处理,因此可以使用一些帮助。我正在重用this jpeg decoder 作为第三方帮助库。在示例的 bmp 解码中,我不太明白计算 row_sizes 和获取字节数组 after the header 是什么意思。谁能阐明这将如何适用于 jpeg 解码器?或者,更好的是,是否已经有一个 C++ decode_jpeg 函数隐藏在我没有找到的某个地方?

最终实现必须在 C++ 中的 TensorFlow Lite 中。

非常感谢!

编辑:

以下是我目前所拥有的。当我对相同的输入图像和 tflite 模型使用图像分类器的 Python 示例时,我没有得到相同的置信度值,因此这清楚地表明出现了问题。我基本上从 read_bmp 复制并粘贴了 row_size 计算但没有理解它,所以我怀疑这可能是问题所在。 row_size 代表什么?

std::vector<uint8_t> decode_jpeg(const uint8_t* input, int row_size, int width, int height) 

    // Channels will always be 3. Hardcode it for now.
    int channels = 3;

    // The output that wil lcontain the data for TensorFlow to process.
    std::vector<uint8_t> output(height * width * channels);

    // Go through every pixel of the image.
    for(int i = 0; i < height; i++) 
            int src_pos;
            int dst_pos;

            for(int j = 0; j < width; j++) 

                    src_pos = i * row_size + j * channels;
                    dst_pos = (i * width + j) * channels;

                    // Put RGB channel data into the output array.
                    output[dst_pos] = input[src_pos + 2];
                    output[dst_pos + 1] = input[src_pos + 1];
                    output[dst_pos + 2] = input[src_pos];
            
    

    return output;


std::vector<uint8_t> read_jpeg(const std::string& input_jpeg_name, int* width, int* height, Settings* s) 

    // Size and buffer.
    size_t size;
    unsigned char *buf;

    // Open the input file.
    FILE *f;
    f = fopen(input_jpeg_name.c_str(), "rb");
    if (!f) 
            if (s->verbose) LOG(INFO) << "Error opening the input file\n";
            exit(-1);
    

    // Read the file.
    fseek(f, 0, SEEK_END);

    // Ge tthe file size.
    size = ftell(f);

    // Get file data into buffer.
    buf = (unsigned char*)malloc(size);
    fseek(f, 0, SEEK_SET);
    size_t read = fread(buf, 1, size, f);
    
    // Close the file.
    fclose(f);

    // Decode the file.
    Decoder decoder(buf, size);
    if (decoder.GetResult() != Decoder::OK)
    
            if (s->verbose) LOG(INFO) << "Error decoding the input file\n";
            exit(-1);
    

    // Get the image from the decoded file.
    unsigned char* img = decoder.GetImage();

    // Get image width and height.
    *width = decoder.GetWidth();
    *height = decoder.GetHeight();

    // TODO: Understand what this row size means. Don't just copy and paste.
    const int row_size = (8 * *channels * *width + 31) / 32 * 4;

    // Decode the JPEG.
    return decode_jpeg(img, row_size, *width, *height);

【问题讨论】:

您能否按照您提供的 jpeg 解码器链接中的建议,使用 ImageMagick 或类似工具提前将它们转换为 BMP?如果是这样,您可以按原样使用该示例。如果不是,并且如果 TensorFlow Lite 仅 groks BMP,您可能会搜索功能更全的图像库,以帮助您从不同的文件格式中获取位图数据,以便您可以专注于算法的细节。 ImageMagick 可能是候选人。 感谢您的建议!不幸的是,这不是一种选择,因为在处理复杂性和磁盘空间方面,操作环境受到严格限制。我必须解码给定的 jpg 文件。 您仍然可以在运行时对其进行解码,同时使用不同的库来提供更接近您可以使用的结果,不是吗?我不清楚为什么你需要为 JPEG 执行它目前为 BMP 执行的步骤。这两种格式不会有相同的标题或结构。尝试准确显示您要解决的问题。 TFL 是否对内存中的原始 RGB 数组进行操作?它是否接受您必须实现以满足其规范的图像加载功能?请发布(摘录而不是链接)您遇到问题的代码和您尝试遇到的界面。 感谢您的关注!我的理解是我只需要获取每个像素的 RGB 值并将它们放入输出数组中,因此我需要没有标题的图像数据。我知道来自mini-jpeg-decoder 的 decoder.GetImage() 会跳过标头数据,所以我应该只剩下要解码的图像字节。我的理解可能有误!我真的无法在目标环境中安装 ImageMagick 之类的东西,它需要尽可能轻量级。我在原始帖子中包含了一些摘录。谢谢! 【参考方案1】:

您正在使用的库已经在为您处理解码,decoder.getImage() 包含原始 rgb 数据。您无需计算任何尺寸。

row_size 之类的东西是 BMP 文件格式特有的。除了像素颜色数据之外,BMP 文件可能包含一些填充字节,代码正在处理这些内容。

BMP 文件也以 BGR 顺序存储像素值,这就是您在原始代码中使用反向排序的原因:

// Put RGB channel data into the output array.
output[dst_pos] = input[src_pos + 2];
output[dst_pos + 1] = input[src_pos + 1];
output[dst_pos + 2] = input[src_pos];

下面的代码应该对你有用(注意 decode_jpeg 函数不执行任何解码):

std::vector<uint8_t> decode_jpeg(const uint8_t* input, int width, int height) 

    // Channels will always be 3. Hardcode it for now.
    int channels = 3;

    // The output that will contain the data for TensorFlow to process.
    std::vector<uint8_t> output(height * width * channels);

    //  Copy pixel data to output
    for (size_t i = 0; i < height*width*channels; ++i)
    
        output[i] = input[i];
    

    
    return output;


std::vector<uint8_t> read_jpeg(const std::string& input_jpeg_name, int* width, int* height, Settings* s) 

    // Size and buffer.
    size_t size;
    unsigned char *buf;

    // Open the input file.
    FILE *f;
    f = fopen(input_jpeg_name.c_str(), "rb");
    if (!f) 
            if (s->verbose) LOG(INFO) << "Error opening the input file\n";
            exit(-1);
    

    // Read the file.
    fseek(f, 0, SEEK_END);

    // Ge tthe file size.
    size = ftell(f);

    // Get file data into buffer.
    buf = (unsigned char*)malloc(size);
    fseek(f, 0, SEEK_SET);
    size_t read = fread(buf, 1, size, f);
    
    // Close the file.
    fclose(f);

    // Decode the file.
    Decoder decoder(buf, size);
    if (decoder.GetResult() != Decoder::OK)
    
            if (s->verbose) LOG(INFO) << "Error decoding the input file\n";
            exit(-1);
    

    // Get the image from the decoded file.
    unsigned char* img = decoder.GetImage();

    // Get image width and height.
    *width = decoder.GetWidth();
    *height = decoder.GetHeight();

    // Decode the JPEG.
    return decode_jpeg(img, *width, *height);

【讨论】:

非常感谢您的图像处理课程,一切都清楚了!所以 decode_jpeg 应该重命名为 to_vector 之类的东西。或者也许我可以改用 std::transform 。但是,当使用与label_image 的 python 实现相同的图像和模型时,我的置信水平仍然不匹配,所以有些事情仍然存在! 也许首先比较 C++ 代码和 python 代码(在任意像素位置)的一些像素值,以确保您得到正确的 RGB 值。如果它们相同,则火车代码之间可能存在差异。 @Georges 这是一个示例代码,它使用您的 jpeg 解码库来访问图像的像素值。我用一个简单的 10x10 图像进行了测试,似乎可以正常工作。 pastebin.com/c4ugM8fe(出于某种原因,我似乎缺少标题,它们是 stdio.h、stdlib.h、string.h)。 这超出了我们所需要的。显然你已经回答了我原来的问题。我在信心水平上的差异是另一个问题。非常感谢!

以上是关于为 TensorFlow Lite C++ 编写 read_jpeg 和 decode_jpeg 函数的主要内容,如果未能解决你的问题,请参考以下文章

如何设置 Tensorflow Lite C++ 的输入

用于推理的 TensorFlow Lite C++ API 示例

如何将 TensorFlow Lite 构建为静态库并从单独的(CMake)项目链接到它?

如何在 c++ 项目中使用 TF Lite 库

tensorflow lite添加自定义操作

如何将冻结图转换为 TensorFlow lite