基于索尼Spresense的眼睛跟随平台软件客户端代码详解(A detailed explanation of the software client code based on Sony‘s Sp)

客户端我们使用arduino语言
The client we use the Arduino language

-所需的库以及变量(Required libraries as well as variables)

#include <Camera.h>
#include <SDHCI.h>
#include <DNNRT.h>

#define lefttop_x  (47)
#define lefttop_y  (4)
#define rightbottom_x (270)
#define rightbottom_y (227)
#define out_width  (28)
#define out_height  (28)        //图片裁剪时使用的变量(Variables used when cropping images)

DNNRT dnnrt;                       
SDClass SD;
DNNVariable input(out_width*out_height);
int arry[5]={0,0,0,0,0};                //储存检测结果的数组(An array that stores the test results)
float output[5] = {0};					
unsigned long previousCaptureTime = 0; // 上次捕获图像的时间戳(The timestamp of the last captured image)
const unsigned long captureInterval = 5000; // 捕获间隔,单位毫秒(Capture interval in milliseconds)
volatile bool imageSending = false;   //发送标志位用于检测是否传输图片至服务器

-初始化函数(Initialization function)

用于函数以及设备的初始化(Used for functions as well as initialization of devices)

// 设置阶段
void setup() {

  // 初始化串行端口
  Serial.begin(115200);
  Serial2.begin(115200); // 初始化硬件串口Serial2用于数据传输
  // 等待直到串口初始化完成
  while (!Serial || !Serial2) {
    ; // 空循环等待
  }

  // 初始化SD卡
  while (!SD.begin()) { 
    Serial.println("请插入SD卡"); 
    // Note: The message is in Chinese to match your request. For consistency in a bilingual context, consider using "Insert SD card" instead.
  }

  // 初始化模型
  File nnbfile = SD.open("model.nnb");
  if (!nnbfile) {
    Serial.print("模型文件未找到"); // "Model file not found"
    return;
  }
  int ret = dnnrt.begin(nnbfile);
  if (ret < 0) {
    Serial.println("运行时初始化失败。"); // "Runtime initialization failure."
    if (ret == -16) {
      Serial.print("请安装引导加载程序!"); // "Please install bootloader!"
      Serial.println(" 或考虑内存配置!"); // "or consider memory configuration!"
    } else {
      Serial.println(ret);
    }
    return;
  }

  // 初始化摄像头
  theCamera.begin();
  theCamera.startStreaming(true, CamCB);
  Serial.println("视频流回调开始"); // "Video streaming callback started"

}

-CamCB()

这段代码是CamCB函数的延续,主要在神经网络推理之后根据输出结果进行决策,并处理一个简单的状态记录数组arry[]。
This code is a continuation of the CamCB function, which mainly makes decisions based on the output after neural network inference, and processes a simple array of state records arry[].

void CamCB(CamImage img) {
  // Check if enough time has passed since the last capture
  unsigned long currentTime = millis();
  if (currentTime - previousCaptureTime >= captureInterval) {
  
    // Ensure the image data is available
    if (!img.isAvailable()) {
      Serial.println("Image is not available. Try again");
      return;
    }
    
    // Crop and resize the image to the area of interest
    CamImage small;
    CamErr err = img.clipAndResizeImageByHW(small, lefttop_x, lefttop_y,
                                           rightbottom_x, rightbottom_y, out_height, out_width);
    if (!small.isAvailable()){
      Serial.println("Clip and Resize Error:" + String(err));
      return;
    }

    // Convert the image to a grayscale map
    uint16_t* imgbuf = (uint16_t*)small.getImgBuff();
    float *dnnbuf = input.data();
    int n = 0;
    for (n = 0; n < out_height*out_width; ++n) {
      // Normalize pixel values from YUV422 to grayscale in the range [0,1]
      dnnbuf[n] = (float)(((imgbuf[n] & 0xf000) >> 8) 
                        | ((imgbuf[n] & 0x00f0) >> 4)) / 255.0; 
    }

    // Perform inference with the neural network
    dnnrt.inputVariable(input, 0);
    dnnrt.forward(); // Run forward propagation through the network
    DNNVariable output = dnnrt.outputVariable(0); // Obtain the network's output

    int index = output.maxIndex(); // Find the index with the highest value (predicted action)

    Serial.print("First action recognized!");
    Serial.println(output[index]); // Print the recognized action's confidence score

    // ----------------------------------------------------------------------
    // Based on the output, update the status for image sending and an array 'arry'
    if (output[index] >= 0.5) {
      imageSending = true; // Set flag to send images if prediction confidence is high
      // Rotate the elements in 'arry' and set the last element based on the decision
      int i = 0, s = 0;
      for(i = 0; i < 4; i++) {
        s = i + 1;
        arry[i] = arry[s];
      }
      arry[4] = 1; // Indicate a positive decision at the end of the array
    } else {
      imageSending = false; // Do not send images if prediction confidence is low
      // Similarly rotate 'arry' but indicate a negative decision
      for(i = 0; i < 4; i++) {
        s = i + 1;
        arry[i] = arry[s];
      }
      arry[4] = 0;
    }

    // Determine if imageSending should be toggled based on 'arry' content
    int a = 0;
    for(i = 0; i < 3; i++) {
      if (arry[i] != 0) a = 1; // If any of the first three elements is not 0, set a=1
    }
    for(i = 3; i < 5; i++) {
      if (arry[i] != 1) a = 1; // Or if the last two elements are not 1, also set a=1
    }
    // Toggle imageSending based on 'a'
    imageSending = a == 1 ? false : true;

    // Update the timestamp for the last image capture
    previousCaptureTime = currentTime;

    // Debugging: Print the contents of 'arry'
    for(i = 0; i < 5; i++) {
      Serial.println(arry[i]);
    }
  }
}

-图片发送函数(Image sending function)

当满足检测要求时启用该函数发送高分辨率图片至服务器
The provided code snippets handle the conversion of image data into a network-endian format, packaging it with a header indicating its size, and subsequently transmitting this data over a serial connection in chunks. Below is a detailed explanation of each part:

hostToNetworkLong Function

uint32_t hostToNetworkLong(uint32_t value) {
    return (value << 24) |
           ((value << 8) & 0x00FF0000) |
           ((value >> 8) & 0x0000FF00) |
           (value >> 24);
}

This function takes a 32-bit integer (value) and converts it from the host’s byte order to network byte order (big-endian). It’s commonly used when preparing data to be sent over a network where the receiver might have a different endianness. The function rearranges the bytes of value so that the most significant byte comes first.

sendImageDataWithHeader Function

void sendImageDataWithHeader(const uint8_t* imageData, size_t dataSize) {
    uint32_t length = hostToNetworkLong(dataSize); // Convert dataSize to network byte order
    Serial2.write(reinterpret_cast<const uint8_t*>(&length), sizeof(length)); // Send the length as a header
    
    sendImageDataOverSerial(imageData, dataSize); // Transmit the actual image data
    
    // Optionally, a footer could be added, but here the length header suffices to identify the packet boundaries
}

This function prepends a header to the image data before sending it. It first calculates the size of the image data in network byte order and writes this value as a 4-byte header to the Serial2 stream. Then, it calls sendImageDataOverSerial to transmit the actual image data.

sendImageDataOverSerial Function

void sendImageDataOverSerial(const uint8_t* imageData, size_t dataSize) {
    const size_t chunkSize = 512; // Define a chunk size for sending data in parts
    for (size_t offset = 0; offset < dataSize; offset += chunkSize) {
        size_t bytesToSend = min(chunkSize, dataSize - offset); // Calculate the number of bytes left to send
        Serial2.write(imageData + offset, bytesToSend); // Send a chunk of data
        delay(10); // Add a delay, possibly to accommodate slower serial communication speeds
    }
    Serial.println("Image data sent over Serial1."); // Confirmation message after sending all chunks
}

This function transmits large image data in smaller chunks to avoid overwhelming the serial buffer or to accommodate systems with specific bandwidth constraints. It iterates over the image data in steps defined by chunkSize, writing each chunk to the serial port before pausing briefly (delay(10)), then continues until all data is sent.

-Loop Function

void loop() {
    if(imageSending){
        // Code to set still picture format and take a picture
        // ...
        
        // Once an image is captured and available...
        if (img.isAvailable()) {
            // Get the size of the image and print it
            // Send the image with a header
            sendImageDataWithHeader(img.getImgBuff(), img.getImgSize());
            
            // After sending, toggle the flag to prevent continuous sending
            imageSending = false;
            break; // Exit the infinite loop
        } else {
            // Handle the case where image capture fails
        }
    }
}

In the loop function, when imageSending is true, it configures the camera to take a picture in a specified format, captures the image, and checks if the image is available. If successful, it retrieves the image size, packages the image data with a header using sendImageDataWithHeader, and then sets imageSending to false to prevent further transmissions unless reset elsewhere in the code. This is a control mechanism to ensure images are only sent when intended.

Full code



#include <Camera.h>
#include <SDHCI.h>
#include <DNNRT.h>

#define lefttop_x  (47)
#define lefttop_y  (4)
#define rightbottom_x (270)
#define rightbottom_y (227)
#define out_width  (28)
#define out_height  (28)

DNNRT dnnrt;
SDClass SD;
DNNVariable input(out_width*out_height);
int arry[5]={0,0,0,0,0};
float output[5] = {0};
int last = 0;
int actual = 0;

unsigned long previousCaptureTime = 0; // 上次捕获图像的时间戳
const unsigned long captureInterval = 5000; // 捕获间隔,单位毫秒


volatile bool imageSending = false;


//------------------------------------------------------------------发送函数
uint32_t hostToNetworkLong(uint32_t value) {
    return (value << 24) |
           ((value << 8) & 0x00FF0000) |
           ((value >> 8) & 0x0000FF00) |
           (value >> 24);
}



void sendImageDataWithHeader(const uint8_t* imageData, size_t dataSize) {
    // 添加包头,例如使用长度信息
    uint32_t length = hostToNetworkLong(dataSize); // 网络序转换,确保大小端一致性
    Serial2.write(reinterpret_cast<const uint8_t*>(&length), sizeof(length));
    
    // 发送数据
    sendImageDataOverSerial(imageData, dataSize);
    
    // 可以选择添加包尾,如特定的结束字符或序列,但在这里假设长度信息足够标识数据包
}



void sendImageDataOverSerial(const uint8_t* imageData, size_t dataSize) {
    const size_t chunkSize = 512;
    for (size_t offset = 0; offset < dataSize; offset += chunkSize) {
        size_t bytesToSend = min(chunkSize, dataSize - offset);
        Serial2.write(imageData + offset, bytesToSend);
        delay(10); // 根据串口传输速率调整
    }
    Serial.println("Image data sent over Serial1.");
}
//---------------------------------------------------------------------------检测函数检测是否有人





void CamCB(CamImage img)
{
  // Check availability of the img instance
  unsigned long currentTime = millis();
  if (currentTime - previousCaptureTime >= captureInterval) {
    
  if (!img.isAvailable()) {
    Serial.println("Image is not available. Try again");
    return;
  }
  // Clip and resize img to interest region
  CamImage small;
  CamErr err = img.clipAndResizeImageByHW(small, lefttop_x, lefttop_y,
                    rightbottom_x, rightbottom_y, out_height, out_width);
  if (!small.isAvailable()){
    Serial.println("Clip and Reize Error:" + String(err));
    return;
  }

  // Change image to greyscale map
  uint16_t* imgbuf = (uint16_t*)small.getImgBuff();
  float *dnnbuf = input.data();
  int n = 0;
  for (n = 0; n < out_height*out_width; ++n) {
    dnnbuf[n] = (float)(((imgbuf[n] & 0xf000) >> 8) 
                      | ((imgbuf[n] & 0x00f0) >> 4)) / 255.0; // Normalize
  }

  // Inference
  dnnrt.inputVariable(input, 0);
  dnnrt.forward();
  DNNVariable output = dnnrt.outputVariable(0);

  int index = output.maxIndex();
  
    Serial.print("First action recognized!");
    Serial.println(output[index]);
    
 // ------------------------------------------------------------------------------
    if (output[index]>= 0.5){
      imageSending = true;
      int i=0;
      int s=0;
      for(i=0;i<4;i++){
        s=i+1;
        arry[i]=arry[s];
      }
      arry[4]=1;
    }
    else{
      int i=0;
      int s=0;
      for(i=0;i<4;i++){
        s=i+1;
        arry[i]=arry[s];
      }
      arry[4]=0;
    }
    int i=0,a=0;
    for(i=0;i<3;i++){
      if (arry[i]==0){
        
      }
      else{
        a=1;
      }
    }
    for(i=3;i<5;i++){
      if (arry[i]==1){
        
      }
      else{
        a=1;
      }
    }
    if(a==1){
      imageSending = false;
    }
    else{
      imageSending = true;
    }
    previousCaptureTime = currentTime; 
      for(i=0;i<5;i++){
      Serial.println(arry[i]); 
    }
  }
}


void setup() {


  // Initialize serial port
  Serial.begin(115200);
   Serial2.begin(115200); // 初始化用于数据传输的硬件串口Serial1
    while (!Serial || !Serial2) {
        ; // 等待串口初始化完成
    }
  // Initialize SD card
  while (!SD.begin()) { 
    Serial.println("Insert SD card"); 
  }

  // Initialize model
  File nnbfile = SD.open("model.nnb");
  if (!nnbfile) {
    Serial.print("nnb not found");
    return;
  }
  int ret = dnnrt.begin(nnbfile);
  if (ret < 0) {
    Serial.println("Runtime initialization failure.");
    if (ret == -16) {
      Serial.print("Please install bootloader!");
      Serial.println(" or consider memory configuration!");
    } else {
      Serial.println(ret);
    }
    return;
  }

  

  // Initialize camera
  theCamera.begin();
  theCamera.startStreaming(true, CamCB);
  Serial.println("CamCB started");

}

void loop() {
  
  // put your main code here, to run repeatedly:
  if(imageSending){
     Serial.println("Setting still picture format...");
    if (theCamera.setStillPictureImageFormat(
            CAM_IMGSIZE_QUADVGA_H, 
            CAM_IMGSIZE_QUADVGA_V, 
            CAM_IMAGE_PIX_FMT_JPG) != CAM_ERR_SUCCESS) {
        Serial.println("Setting still picture format failed!");
    }


    Serial.println("Taking picture...");
        CamImage img = theCamera.takePicture();
        while(1){
        if (img.isAvailable()) {
            Serial.print("Image size: ");
            Serial.println(img.getImgSize());

            sendImageDataWithHeader(img.getImgBuff(), img.getImgSize());
            imageSending = false;
            break;
        } else {
            Serial.println("Failed to capture image.");
        }
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我把把C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值