使用通过 make_image_classifier 创建的模型时,在 TensorFlow Lite C API 中调整输入维度
Resizing the input dimension in TensorFlow Lite C API when using a model created with make_image_classifier
抱歉,如果这个问题看起来很眼熟,我之前 post 对该问题进行了更广泛的描述,但我已经删除了它,因为我在调查中取得了一些进展并且可以缩小到更具体的范围问题。
上下文:
- 我正在使用
make_image_classifier
创建图像分类模型。
- 我想使用 C API 加载生成的模型和标签图像。我在这里遇到数据输入问题。
- 我可以用 label_image.py example 标记图像,所以模型很好,问题出在我使用 C API。
- 如果我理解
make_image_classifier
正确,它会生成一个需要 4 维输入的模型。我们处理的图像超出了宽度、高度和通道,我不知道第四维是什么。这种缺乏理解可能是我问题的根源。
- 我在我的代码中包含了一些错误处理,我遇到的错误发生在调整大小后尝试从输入缓冲区复制时。
问题:
问题 1: 为什么 make_image_classifier
生成的模型需要 4 维输入?有高度、宽度和通道,但第 4 个是什么?
当我对 C API 执行以下操作时 运行 使用我的图像输入的模型:
int inputDims[3] = {224, 224, 3};
tflStatus = TfLiteInterpreterResizeInputTensor(interpreter, 0, inputDims, 3);
我得到:
ERROR: tensorflow/lite/kernels/conv.cc:329 input->dims->size != 4 (3 != 4)
ERROR: Node number 2 (CONV_2D) failed to prepare.
所以我最后做了:
int inputDims[4] = {1, 224, 224, 3};
tflStatus = TfLiteInterpreterResizeInputTensor(interpreter, 0, inputDims, 4);
据我所知,the first dimension size is for the batch size 以防我要处理多个图像。这是正确的吗?
问题 2: 我是否应该在调用 TfLiteInterpreterResizeInputTensor
时使用相同的维度结构构建我的数据输入?我收到此图像 RGB 输入缓冲区的相关错误:
// RGB range is 0-255. Scale it to 0-1.
for(int i = 0; i < imageSize; i++){
imageDataBuffer[i] = (float)pImage[i] / 255.0;
}
在构建模拟给定给 TfLiteInterpreterResizeInputTensor
的输入维度的输入时,我也遇到错误,但这看起来很愚蠢:
float imageData[1][224][224][3];
int j = 0;
for(int h = 0; h < 224; h++){
for(int w = 0; w < 224; w++){
imageData[0][h][w][0] = (float)pImage[j] * (1.0 / 255.0);
imageData[0][h][w][1] = (float)pImage[j+1] * (1.0 / 255.0);
imageData[0][h][w][2] = (float)pImage[j+2] * (1.0 / 255.0);
j = j + 3;
}
}
最后一个输入结构与 Python label_image.py
中使用的输入结构类似:
input_data = np.expand_dims(img, axis=0)
问题 3: 我的输入缓冲区有什么问题导致 TfLiteTensorCopyFromBuffer
return 成为错误代码?
谢谢!
完整代码:
#include "tensorflow/lite/c/c_api.h"
#include "tensorflow/lite/c/c_api_experimental.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/c/builtin_op_data.h"
#include "tensorflow/lite/c/ujpeg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Dispose of the model and interpreter objects.
int disposeTfLiteObjects(TfLiteModel* pModel, TfLiteInterpreter* pInterpreter)
{
if(pModel != NULL)
{
TfLiteModelDelete(pModel);
}
if(pInterpreter)
{
TfLiteInterpreterDelete(pInterpreter);
}
}
// The main function.
int main(void)
{
TfLiteStatus tflStatus;
// Create JPEG image object.
ujImage img = ujCreate();
// Decode the JPEG file.
ujDecodeFile(img, "image_224x224.jpeg");
// Check if decoding was successful.
if(ujIsValid(img) == 0){
return 1;
}
// There will always be 3 channels.
int channel = 3;
// Height will always be 224, no need for resizing.
int height = ujGetHeight(img);
// Width will always be 224, no need for resizing.
int width = ujGetWidth(img);
// The image size is channel * height * width.
int imageSize = ujGetImageSize(img);
// Fetch RGB data from the decoded JPEG image input file.
uint8_t* pImage = (uint8_t*)ujGetImage(img, NULL);
// The array that will collect the JPEG RGB values.
float imageDataBuffer[imageSize];
// RGB range is 0-255. Scale it to 0-1.
int j=0;
for(int i = 0; i < imageSize; i++){
imageDataBuffer[i] = (float)pImage[i] / 255.0;
}
// Load model.
TfLiteModel* model = TfLiteModelCreateFromFile("model.tflite");
// Create the interpreter.
TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, NULL);
// Allocate tensors.
tflStatus = TfLiteInterpreterAllocateTensors(interpreter);
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error allocating tensors.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
int inputDims[4] = {1, 224, 224, 3};
tflStatus = TfLiteInterpreterResizeInputTensor(interpreter, 0, inputDims, 4);
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error resizing tensor.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
tflStatus = TfLiteInterpreterAllocateTensors(interpreter);
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error allocating tensors after resize.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
// The input tensor.
TfLiteTensor* inputTensor = TfLiteInterpreterGetInputTensor(interpreter, 0);
// Copy the JPEG image data into into the input tensor.
tflStatus = TfLiteTensorCopyFromBuffer(inputTensor, imageDataBuffer, imageSize);
// Log and exit in case of error.
// FIXME: Error occurs here.
if(tflStatus != kTfLiteOk)
{
printf("Error copying input from buffer.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
// Invoke interpreter.
tflStatus = TfLiteInterpreterInvoke(interpreter);
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error invoking interpreter.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
// Extract the output tensor data.
const TfLiteTensor* outputTensor = TfLiteInterpreterGetOutputTensor(interpreter, 0);
// There are three possible labels. Size the output accordingly.
float output[3];
tflStatus = TfLiteTensorCopyToBuffer(outputTensor, output, 3 * sizeof(float));
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error copying output to buffer.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
// Print out classification result.
printf("Confidences: %f, %f, %f.\n", output[0], output[1], output[2]);
// Dispose of the TensorFlow objects.
disposeTfLiteObjects(model, interpreter);
// Dispoice of the image object.
ujFree(img);
return 0;
}
编辑 #1: 好的,所以里面 TfLiteTensorCopyFromBuffer
:
TfLiteStatus TfLiteTensorCopyFromBuffer(TfLiteTensor* tensor,
const void* input_data,
size_t input_data_size) {
if (tensor->bytes != input_data_size) {
return kTfLiteError;
}
memcpy(tensor->data.raw, input_data, input_data_size);
return kTfLiteOk;
}
我的 input_data_size
值是 150,528(3 通道 x 224 像素高 x 224 像素宽)但 tensor->bytes
是 602,112(我假设是 3 通道 x 448 像素高 x 224 像素 448?) .我不明白这种差异,特别是因为我用 {1, 224, 224, 3}
.
调用了 TfLiteInterpreterResizeInputTensor
编辑 #2: 我相信我已经找到答案 。将在确认后解决此 post。
我在 EDIT #2 上链接到的解决方案就是答案。最后,我只需要更换:
TfLiteTensorCopyFromBuffer(inputTensor, imageDataBuffer, imageSize);
与:
TfLiteTensorCopyFromBuffer(inputTensor, imageDataBuffer, imageSize * sizeof(float));
干杯!
抱歉,如果这个问题看起来很眼熟,我之前 post 对该问题进行了更广泛的描述,但我已经删除了它,因为我在调查中取得了一些进展并且可以缩小到更具体的范围问题。
上下文:
- 我正在使用
make_image_classifier
创建图像分类模型。 - 我想使用 C API 加载生成的模型和标签图像。我在这里遇到数据输入问题。
- 我可以用 label_image.py example 标记图像,所以模型很好,问题出在我使用 C API。
- 如果我理解
make_image_classifier
正确,它会生成一个需要 4 维输入的模型。我们处理的图像超出了宽度、高度和通道,我不知道第四维是什么。这种缺乏理解可能是我问题的根源。 - 我在我的代码中包含了一些错误处理,我遇到的错误发生在调整大小后尝试从输入缓冲区复制时。
问题:
问题 1: 为什么 make_image_classifier
生成的模型需要 4 维输入?有高度、宽度和通道,但第 4 个是什么?
当我对 C API 执行以下操作时 运行 使用我的图像输入的模型:
int inputDims[3] = {224, 224, 3};
tflStatus = TfLiteInterpreterResizeInputTensor(interpreter, 0, inputDims, 3);
我得到:
ERROR: tensorflow/lite/kernels/conv.cc:329 input->dims->size != 4 (3 != 4)
ERROR: Node number 2 (CONV_2D) failed to prepare.
所以我最后做了:
int inputDims[4] = {1, 224, 224, 3};
tflStatus = TfLiteInterpreterResizeInputTensor(interpreter, 0, inputDims, 4);
据我所知,the first dimension size is for the batch size 以防我要处理多个图像。这是正确的吗?
问题 2: 我是否应该在调用 TfLiteInterpreterResizeInputTensor
时使用相同的维度结构构建我的数据输入?我收到此图像 RGB 输入缓冲区的相关错误:
// RGB range is 0-255. Scale it to 0-1.
for(int i = 0; i < imageSize; i++){
imageDataBuffer[i] = (float)pImage[i] / 255.0;
}
在构建模拟给定给 TfLiteInterpreterResizeInputTensor
的输入维度的输入时,我也遇到错误,但这看起来很愚蠢:
float imageData[1][224][224][3];
int j = 0;
for(int h = 0; h < 224; h++){
for(int w = 0; w < 224; w++){
imageData[0][h][w][0] = (float)pImage[j] * (1.0 / 255.0);
imageData[0][h][w][1] = (float)pImage[j+1] * (1.0 / 255.0);
imageData[0][h][w][2] = (float)pImage[j+2] * (1.0 / 255.0);
j = j + 3;
}
}
最后一个输入结构与 Python label_image.py
中使用的输入结构类似:
input_data = np.expand_dims(img, axis=0)
问题 3: 我的输入缓冲区有什么问题导致 TfLiteTensorCopyFromBuffer
return 成为错误代码?
谢谢!
完整代码:
#include "tensorflow/lite/c/c_api.h"
#include "tensorflow/lite/c/c_api_experimental.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/c/builtin_op_data.h"
#include "tensorflow/lite/c/ujpeg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Dispose of the model and interpreter objects.
int disposeTfLiteObjects(TfLiteModel* pModel, TfLiteInterpreter* pInterpreter)
{
if(pModel != NULL)
{
TfLiteModelDelete(pModel);
}
if(pInterpreter)
{
TfLiteInterpreterDelete(pInterpreter);
}
}
// The main function.
int main(void)
{
TfLiteStatus tflStatus;
// Create JPEG image object.
ujImage img = ujCreate();
// Decode the JPEG file.
ujDecodeFile(img, "image_224x224.jpeg");
// Check if decoding was successful.
if(ujIsValid(img) == 0){
return 1;
}
// There will always be 3 channels.
int channel = 3;
// Height will always be 224, no need for resizing.
int height = ujGetHeight(img);
// Width will always be 224, no need for resizing.
int width = ujGetWidth(img);
// The image size is channel * height * width.
int imageSize = ujGetImageSize(img);
// Fetch RGB data from the decoded JPEG image input file.
uint8_t* pImage = (uint8_t*)ujGetImage(img, NULL);
// The array that will collect the JPEG RGB values.
float imageDataBuffer[imageSize];
// RGB range is 0-255. Scale it to 0-1.
int j=0;
for(int i = 0; i < imageSize; i++){
imageDataBuffer[i] = (float)pImage[i] / 255.0;
}
// Load model.
TfLiteModel* model = TfLiteModelCreateFromFile("model.tflite");
// Create the interpreter.
TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, NULL);
// Allocate tensors.
tflStatus = TfLiteInterpreterAllocateTensors(interpreter);
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error allocating tensors.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
int inputDims[4] = {1, 224, 224, 3};
tflStatus = TfLiteInterpreterResizeInputTensor(interpreter, 0, inputDims, 4);
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error resizing tensor.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
tflStatus = TfLiteInterpreterAllocateTensors(interpreter);
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error allocating tensors after resize.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
// The input tensor.
TfLiteTensor* inputTensor = TfLiteInterpreterGetInputTensor(interpreter, 0);
// Copy the JPEG image data into into the input tensor.
tflStatus = TfLiteTensorCopyFromBuffer(inputTensor, imageDataBuffer, imageSize);
// Log and exit in case of error.
// FIXME: Error occurs here.
if(tflStatus != kTfLiteOk)
{
printf("Error copying input from buffer.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
// Invoke interpreter.
tflStatus = TfLiteInterpreterInvoke(interpreter);
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error invoking interpreter.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
// Extract the output tensor data.
const TfLiteTensor* outputTensor = TfLiteInterpreterGetOutputTensor(interpreter, 0);
// There are three possible labels. Size the output accordingly.
float output[3];
tflStatus = TfLiteTensorCopyToBuffer(outputTensor, output, 3 * sizeof(float));
// Log and exit in case of error.
if(tflStatus != kTfLiteOk)
{
printf("Error copying output to buffer.\n");
disposeTfLiteObjects(model, interpreter);
return 1;
}
// Print out classification result.
printf("Confidences: %f, %f, %f.\n", output[0], output[1], output[2]);
// Dispose of the TensorFlow objects.
disposeTfLiteObjects(model, interpreter);
// Dispoice of the image object.
ujFree(img);
return 0;
}
编辑 #1: 好的,所以里面 TfLiteTensorCopyFromBuffer
:
TfLiteStatus TfLiteTensorCopyFromBuffer(TfLiteTensor* tensor,
const void* input_data,
size_t input_data_size) {
if (tensor->bytes != input_data_size) {
return kTfLiteError;
}
memcpy(tensor->data.raw, input_data, input_data_size);
return kTfLiteOk;
}
我的 input_data_size
值是 150,528(3 通道 x 224 像素高 x 224 像素宽)但 tensor->bytes
是 602,112(我假设是 3 通道 x 448 像素高 x 224 像素 448?) .我不明白这种差异,特别是因为我用 {1, 224, 224, 3}
.
TfLiteInterpreterResizeInputTensor
编辑 #2: 我相信我已经找到答案
我在 EDIT #2 上链接到的解决方案就是答案。最后,我只需要更换:
TfLiteTensorCopyFromBuffer(inputTensor, imageDataBuffer, imageSize);
与:
TfLiteTensorCopyFromBuffer(inputTensor, imageDataBuffer, imageSize * sizeof(float));
干杯!