/* Copyright 2022 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #include #include #include "image_provider.h" #include "model_settings.h" #include "tensorflow/lite/micro/micro_log.h" #include "tensorflow/lite/micro/micro_utils.h" #include "test_over_serial/test_over_serial.h" using namespace test_over_serial; #if defined(ARDUINO) && !defined(ARDUINO_ARDUINO_NANO33BLE) #define ARDUINO_EXCLUDE_CODE #endif // defined(ARDUINO) && !defined(ARDUINO_ARDUINO_NANO33BLE) #ifndef ARDUINO_EXCLUDE_CODE #include "Arduino.h" namespace { constexpr size_t kQQVGA_width = 160; // pixels constexpr size_t kQQVGA_height = 120; // pixels uint8_t image_buffer[kQQVGA_height * kQQVGA_width]; constexpr size_t kImageBufferLength = std::extent::value; // Get the camera module ready TfLiteStatus InitCamera() { // This function kept for future implementation MicroPrintf("OV7675 not yet supported. Blank image will be substituted."); return kTfLiteOk; } // Begin the capture and wait for it to finish TfLiteStatus PerformCapture() { // This function kept for future implementation MicroPrintf("Starting capture"); delay(50); MicroPrintf("Image captured"); return kTfLiteOk; } // Read data from the camera module into a local buffer TfLiteStatus ReadData() { // This function kept for future implementation // until OV7675 supported, just fill with zeros (black image) std::fill_n(image_buffer, kImageBufferLength, 0); return kTfLiteOk; } // Decode the image, crop it, and convert it to grayscale TfLiteStatus CropAndQuantizeImage(size_t image_width, size_t image_height, const TfLiteTensor* tensor) { MicroPrintf("Cropping image and quantizing"); // cropping parameters const size_t vert_top = (image_height - kNumRows) / 2; const size_t vert_bottom = vert_top + kNumRows - 1; const size_t horz_left = (image_width - kNumCols) / 2; const size_t horz_right = horz_left + kNumCols - 1; const uint8_t* p = image_buffer + (vert_top * image_width); p += horz_left; int8_t* image_data = tensor->data.int8; for (size_t line = vert_top; line <= vert_bottom; line++) { for (size_t row = horz_left; row <= horz_right; row++, p++) { *image_data++ = tflite::FloatToQuantizedType( p[0] / 255.0f, tensor->params.scale, tensor->params.zero_point); } // move to next line p += ((image_width - 1) - horz_right) + horz_left; } MicroPrintf("Image cropped and quantized"); return kTfLiteOk; } // Get an image from the camera module TfLiteStatus GetCameraImage(const TfLiteTensor* tensor) { static bool g_is_camera_initialized = false; if (!g_is_camera_initialized) { TfLiteStatus init_status = InitCamera(); if (init_status != kTfLiteOk) { MicroPrintf("InitCamera failed"); return init_status; } g_is_camera_initialized = true; } TfLiteStatus capture_status = PerformCapture(); if (capture_status != kTfLiteOk) { MicroPrintf("PerformCapture failed"); return capture_status; } TfLiteStatus read_data_status = ReadData(); if (read_data_status != kTfLiteOk) { MicroPrintf("ReadData failed"); return read_data_status; } TfLiteStatus decode_status = CropAndQuantizeImage(kQQVGA_width, kQQVGA_height, tensor); if (decode_status != kTfLiteOk) { MicroPrintf("CropAndQuantizeImage failed"); return decode_status; } return kTfLiteOk; } TfLiteStatus GetTestImage(TestOverSerial& test, const TfLiteTensor* tensor) { volatile bool done = false; volatile bool aborted = false; volatile size_t image_width = 0, image_height = 0; InputHandler handler = [&aborted, &done, &image_width, &image_height](const InputBuffer* const input) { if (0 == input->offset) { if ((kQQVGA_height * kQQVGA_width) == input->total) { image_width = kQQVGA_width; image_height = kQQVGA_height; } else if ((kNumCols * kNumRows) == input->total) { image_width = kNumCols; image_height = kNumRows; } else { // image dimensions are not supported, abort input processing aborted = true; return false; } } std::copy_n(input->data.uint8, input->length, &image_buffer[input->offset]); if (input->total == (input->offset + input->length)) { done = true; } return true; }; while (!done) { test.ProcessInput(&handler); if (aborted) { MicroPrintf("Input processing aborted"); return kTfLiteError; } // wait for a full image from serial port before processing if (done) { TfLiteStatus decode_status = CropAndQuantizeImage(image_width, image_height, tensor); if (decode_status != kTfLiteOk) { MicroPrintf("CropAndQuantizeImage failed"); return decode_status; } } } return kTfLiteOk; } } // namespace TfLiteStatus GetImage(const TfLiteTensor* tensor) { TestOverSerial& test = TestOverSerial::Instance(kIMAGE_GRAYSCALE); if (!test.IsTestMode()) { // check serial port for test mode command test.ProcessInput(nullptr); } if (test.IsTestMode()) { return GetTestImage(test, tensor); } else { // get an image from the camera return GetCameraImage(tensor); } // NOTREACHED } #endif // ARDUINO_EXCLUDE_CODE