Real-Time Object Detection Using Camera

Environment Setup

  • Hardware: Neardi LPA3588 development board, USB camera (or other cameras)

  • Software: RK3588 SDK, Neardi LPA3588 Ubuntu image

Source Code Download

Download rknpu2 on the LPA3588 development board:

git clone https://github.com/rockchip-linux/rknpu2

OpenCV Installation

sudo apt install libopencv-dev libopencv-videoio-dev libopencv-video-dev libopencv-imgproc-dev libopencv-highgui-dev

Compilation

Navigate to the rknn_ssd_demo directory and modify CMakeLists.txt. The sample code uses OpenCV3 libraries, so we comment out to use the installed OpenCV4 library.

neardi@LPA3588:~/rknn/rknpu2/examples/rknn_ssd_demo$ git diff .
diff --git a/examples/rknn_ssd_demo/CMakeLists.txt b/examples/rknn_ssd_demo/CMakeLists.txt
index bd236f6..40537c1 100644
--- a/examples/rknn_ssd_demo/CMakeLists.txt
+++ b/examples/rknn_ssd_demo/CMakeLists.txt
@@ -33,7 +33,7 @@ else()
   if(LIB_ARCH STREQUAL "armhf")
     set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/opencv/opencv-linux-armhf/share/OpenCV)
   else()
-    set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/opencv/opencv-linux-aarch64/share/OpenCV)
+    #set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/opencv/opencv-linux-aarch64/share/OpenCV)
   endif()
 endif()
 find_package(OpenCV REQUIRED)

The original rknn_ssd_demo analyzes images, hence single-threaded. Here, we modify it to perform object recognition from camera input, enhancing it with multi-threading. First, create a queue data structure (see attached queue.hpp).

  • Add new header files:

neardi@LPA3588:~/rknn/rknpu2/examples/rknn_ssd_demo$ git diff .
--- a/examples/rknn_ssd_demo/src/main.cc
+++ b/examples/rknn_ssd_demo/src/main.cc
@@ -29,6 +29,19 @@
 #include <fstream>
 #include <iostream>
 
+#include "opencv2/core/core.hpp"
+#include "opencv2/imgcodecs.hpp"
+#include "opencv2/imgproc.hpp"
+#include "opencv2/highgui.hpp"
+
+#include <opencv2/videoio/videoio.hpp>
+#include <opencv2/video.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+
+#include "rknn_api.h"
+#include "ssd.h"
+#include "queue.hpp"
+
 using namespace std;
 using namespace cv;
  • Create two global variables, _idleimgbuf & _imgdata, to store images retrieved from the camera, with a maximum length of 300 pixels.

+/**
+ * create memory pool for images gotten from camera.
+ */
+Queue<cv::Mat*> _idleimgbuf;
+Queue<cv::Mat*> _imgdata;
+
  • Add thread functions to read data from the camera:

bool g_exit = false;
static int camera_thread(int index)
{
    /**
     * load image from camera
     */
    cv::VideoCapture cap;
    cap.open(index);
    if (!cap.isOpened()) {
        return -1;
    }

    cv::Mat first;
    cap >> first;
    for(int i = 0; i < max_img_count; i++) {
	_idleimgbuf.push(new cv::Mat(first));
    }

    while (!g_exit)
    {
	 cv::Mat* pimg = NULL;
        _idleimgbuf.pop(pimg);
	if (NULL != pimg) {
            cap >> *pimg;
	    if (pimg->empty()) {
		_idleimgbuf.push(pimg);
	    } else {
		_idleimgbuf.push(pimg);
            }
	} else {
	   /**
            * wait a moment to avoid consume high CPU performance.
	    */
	   usleep(100);
	   continue;
	}
    }
    cap.release();

    return 0;
}

Here, we read the first frame from the camera to determine its frame size.

  • Modify input parameters and start thread code:

/*-------------------------------------------
                  Main Function
-------------------------------------------*/
int main(int argc, char** argv)
{
  const int      img_width    = 300;
  const int      img_height   = 300;
  const int      img_channels = 3;
  int            ret          = 0;
  int            model_len    = 0;
  unsigned char* model        = nullptr;
  rknn_context ctx = 0;

  const char* model_path = argv[1];
  if (argc != 3) {
    printf("Usage:%s model camera\n", argv[0]);
    return -1;
  }

  int index = std::stoi(argv[2]);

    std::thread camthread(&camera_thread, index);

  // Load RKNN Model
  printf("Loading model ...\n");
  model = load_model(model_path, &model_len);

  ...

  return 0;
  • Change the main function to read data:

  namedWindow("Video", 1);
    do {
        cv::Mat* pimg = nullptr;
	_idleimgbuf.pop(pimg);
	if (nullptr == pimg) {
	    usleep(100);
	    if (waitKey(30) >= 0) {
		g_exit = true;
		break;
	    }
	    continue;
	}


        // if origin model is from Caffe, you maybe not need do BGR2RGB.
	cv::Mat orig_img = pimg->clone();
	_idleimgbuf.push(pimg);

        cv::Mat orig_img_rgb;
        cv::cvtColor(orig_img, orig_img_rgb, cv::COLOR_BGR2RGB);
        cv::Mat img = orig_img_rgb.clone();

        if (orig_img_rgb.cols != img_width || orig_img_rgb.rows != img_height) {
            printf("resize %d %d to %d %d\n", orig_img_rgb.cols, orig_img_rgb.rows, img_width, img_height);
            cv::resize(orig_img_rgb, img, cv::Size(img_width, img_height), 0, 0, cv::INTER_LINEAR);
        }
  • Release resources:

/**
     * free the queue buffer.
     */
    while(_idleimgbuf.size() > 0)
    {
        cv::Mat* pmat = NULL;
        _idleimgbuf.pop(pmat);
        if (NULL != pmat) {
            delete pmat;
        }
    }

	while(_imgdata.size() > 0)
    {
        cv::Mat* pmat = NULL;
        _imgdata.pop(pmat);
        if (NULL != pmat) {
            delete pmat;
        }
    }
  • Compile and run rknn_ssd_demo:

./rknn_ssd_demo ./model/RK3588/ssd_inception_v2.rknn 41

Here, 41 refers to the index of /dev/video41 device. Adjust according to your camera input. Detection results are displayed as follows:

../../_images/model_rknn_opencv.png

Demo Download Link