为 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++ API 示例