Given an image buffer, resize it to the requested size, and then scale the values as desired.
| 329 | // Given an image buffer, resize it to the requested size, and then scale the |
| 330 | // values as desired. |
| 331 | Status TensorFromFrame(uint8_t* image_data, int image_width, int image_height, |
| 332 | int image_channels, const int wanted_height, |
| 333 | const int wanted_width, const float input_mean, |
| 334 | const float input_std, |
| 335 | std::vector<Tensor>* out_tensors) { |
| 336 | const int wanted_channels = 3; |
| 337 | if (image_channels < wanted_channels) { |
| 338 | return tensorflow::errors::FailedPrecondition( |
| 339 | "Image needs to have at least ", wanted_channels, " but only has ", |
| 340 | image_channels); |
| 341 | } |
| 342 | // In these loops, we convert the eight-bit data in the image into float, |
| 343 | // resize it using bilinear filtering, and scale it numerically to the float |
| 344 | // range that the model expects (given by input_mean and input_std). |
| 345 | tensorflow::Tensor image_tensor( |
| 346 | tensorflow::DT_FLOAT, |
| 347 | tensorflow::TensorShape( |
| 348 | {1, wanted_height, wanted_width, wanted_channels})); |
| 349 | auto image_tensor_mapped = image_tensor.tensor<float, 4>(); |
| 350 | tensorflow::uint8* in = image_data; |
| 351 | float* out = image_tensor_mapped.data(); |
| 352 | const size_t image_rowlen = image_width * image_channels; |
| 353 | const float width_scale = static_cast<float>(image_width) / wanted_width; |
| 354 | const float height_scale = static_cast<float>(image_height) / wanted_height; |
| 355 | for (int y = 0; y < wanted_height; ++y) { |
| 356 | const float in_y = y * height_scale; |
| 357 | const int top_y_index = static_cast<int>(floorf(in_y)); |
| 358 | const int bottom_y_index = |
| 359 | std::min(static_cast<int>(ceilf(in_y)), (image_height - 1)); |
| 360 | const float y_lerp = in_y - top_y_index; |
| 361 | tensorflow::uint8* in_top_row = in + (top_y_index * image_rowlen); |
| 362 | tensorflow::uint8* in_bottom_row = in + (bottom_y_index * image_rowlen); |
| 363 | float* out_row = out + (y * wanted_width * wanted_channels); |
| 364 | for (int x = 0; x < wanted_width; ++x) { |
| 365 | const float in_x = x * width_scale; |
| 366 | const int left_x_index = static_cast<int>(floorf(in_x)); |
| 367 | const int right_x_index = |
| 368 | std::min(static_cast<int>(ceilf(in_x)), (image_width - 1)); |
| 369 | tensorflow::uint8* in_top_left_pixel = |
| 370 | in_top_row + (left_x_index * wanted_channels); |
| 371 | tensorflow::uint8* in_top_right_pixel = |
| 372 | in_top_row + (right_x_index * wanted_channels); |
| 373 | tensorflow::uint8* in_bottom_left_pixel = |
| 374 | in_bottom_row + (left_x_index * wanted_channels); |
| 375 | tensorflow::uint8* in_bottom_right_pixel = |
| 376 | in_bottom_row + (right_x_index * wanted_channels); |
| 377 | const float x_lerp = in_x - left_x_index; |
| 378 | float* out_pixel = out_row + (x * wanted_channels); |
| 379 | for (int c = 0; c < wanted_channels; ++c) { |
| 380 | const float top_left((in_top_left_pixel[c] - input_mean) / input_std); |
| 381 | const float top_right((in_top_right_pixel[c] - input_mean) / input_std); |
| 382 | const float bottom_left((in_bottom_left_pixel[c] - input_mean) / |
| 383 | input_std); |
| 384 | const float bottom_right((in_bottom_right_pixel[c] - input_mean) / |
| 385 | input_std); |
| 386 | const float top = top_left + (top_right - top_left) * x_lerp; |
| 387 | const float bottom = |
| 388 | bottom_left + (bottom_right - bottom_left) * x_lerp; |
no test coverage detected