一、交叉编译OpenCV4.5.5

本文使用的环境是ubuntu20.04的x86_64架构

$ uname -a
Linux kfb-sy08-031 5.15.0-69-generic #76~20.04.1-Ubuntu SMP Mon Mar 20 15:54:19 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

1.1 安装相关工具

# 安装相关本地依赖库
sudo apt-get install -y unzip bzip2 expat gpgv2 cpp-aarch64-linux-gnu g++-aarch64-linux-gnu
sudo apt-get install build-essential libgtk2.0-dev libgtk-3-dev libavcodec-dev libavformat-dev libjpeg-dev libswscale-dev libtiff5-dev

# 安装cmake、cmake-gui
sudo apt-get install cmake cmake-qt-gui

1.2 下载源相关源代码

OpenCV4.5.5下载link OpenCV_contrib link

# 创建目录,通过命令下载
cd ~ && mkdir -p ai/opencv/ && cd ~/ai/opencv
# 下载和解压
wget -O opencv.zip https://github.com/opencv/opencv/archive/4.x.zip
wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/4.x.zip
unzip opencv.zip
unzip opencv_contrib.zip
# 或
git clone https://github.com/opencv/opencv.git
git clone https://github.com/opencv/opencv_contrib.git
mv opencv opencv455 && cd opencv455
git checkout 4.5.5 
# 创建编译目录
mkdir -p aarch64_build && cd aarch64_build

1.3 配置cmake环境(使用cmake-gui)

# 启动cmake-gui
cmak-gui
# where is the source code: 就是刚下载的OpenCV解压文件夹
/home/hgh/ai/opencv/opencv-4.5.5
# where to build the binaries: 就是通过上面的OpenCV文件夹编译的保存结果
/home/hgh/ai/opencv/opencv-4.5.5/arch_build
# 增加变量 点击Add Entry 
Name: CMAKE_AR
Type: FILEPATH
Value: /usr/bin/aarch64-linux-gnu-ar
# windows 使用mingw
Value D:/ProgramData/mingw64/bin/ar.exe
#下一步的
"Specify the generator for this project""MinGW Makefiles"
# gcc和g++ 选择自己安装的mingw中bin目录下的gcc.exe 和 g++.exe

# Value 通过which命令查询路径
$ which aarch64-linux-gnu-ar 
/usr/bin/aarch64-linux-gnu-ar
# Windows可以安装everthing软件,搜万物!

链接: link.
在这里插入图片描述
图片:
点击Next 跳到下图界面,选择自己下载的OpenCV解压包中的文件,默认会跳转到选择对应的文件夹中
在这里插入图片描述

在Search中搜索: WITH_GTK 勾选选项
在这里插入图片描述

继续搜索: OPENCV_EXTRA_MODULES_PATH 编译使用扩展包路径(增加OpenCV_contrib模块)
/home/hgh/ai/opencv/opencv_contrib-4.5.5/modules
在这里插入图片描述

查看安装路径:CMAKE_INSTALL_PREFIX (一般为编译文件夹下的install)
在这里插入图片描述

1.4 生成OpenCV的makefile配置文件

点击: Configure
再点击:Generate
检查是否报错,网络原因下载某些包问题,多生成几次。不报错可以用终端进行下面操作!

1.5 终端make编译和安装

cd ~/ai/opencv/opencv-4.5.5/arch_build
make -j8
 # 按需是否需要安装编译好的OpenCV库
make install

1.6 使用cmake测试

mkdir rknn_demo && cd rknn_demo
touch CMakeLists.txt

CmakeLists.txt 内容【 cmake学习参考:cmake菜谱

# 设置cmake版本要求
cmake_minimum_required(VERSION 3.4.1)
# 项目工程名称
project(test_opencv)
# 设置编译环境、架构工具  (下面这个设置了,本地的x86架构不能运行)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)

message("CMAKE_SOURCE_DIR is ====> ${CMAKE_SOURCE_DIR}")
# set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../opencv/opencv-linux-aarch64/share/OpenCV)   # set(OpenCV_DIR /home/hgh/source/code/face_detection/yolov5-face-landmarks-opencv-v2/3rdparty/opencv/opencv-linux-aarch64/share/OpenCV)

set(OpenCV_DIR /home/hgh/ai/opencv/opencv-4.5.5/arch_build/install/share/opencv4)

# 查找默认环境的OpenCV
find_package(OpenCV REQUIRED)
# include_directories( ${OpenCV_INCLUDE_DIRS})

# # 自己本地编译的opencv_aarch64版本
# set(OpenCV_INCLUDE_DIRS /home/hgh/ai/opencv/opencv-4.5.5/arch_build/install/include/opencv4)
message("OpenCV_DIR is --> ${OpenCV_DIR}")

# # 引用头文件
# include_directories(BEFORE /home/hgh/ai/opencv/opencv-4.5.5/arch_build/install/include/opencv4)
# include_directories(BEFORE /home/hgh/ai/opencv/opencv-4.5.5/arch_build/install/include/opencv4/opencv2)
# # 引用库文件
link_directories(/home/hgh/ai/opencv/opencv-4.5.5/arch_build/install/lib)
message("link_directories is --> ")

# 打印相关信息
message(STATUS "opencv library status: ")
message(STATUS "\t version ${OpenCV_VERSION}")
message(STATUS "\t libraries: ${OpenCV_LIBS}")
message(STATUS "\t include path ${OpenCV_INCLUDE_DIRS}")
message("---------------------------------------")

add_executable(opencv_test 
                test.cpp
)   
# 若上面的 link_directories 指定文件夹中没有对应的链接库,那么会到本地环境搜索对应的库,但是不同架构的so会报错
target_link_libraries(opencv_test 
    ${OpenCV_LIBS}
)
# 也可以这样设置
# target_link_libraries(yolo_test stdc++ opencv_core opencv_imgproc opencv_imgcodecs opencv_dnn dl rt)

test.cpp 通过官方demo修改的


#include <iostream>

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

#define w 400
using namespace cv;
void MyEllipse( Mat img, double angle );
void MyFilledCircle( Mat img, Point center );
void MyPolygon( Mat img );
void MyLine( Mat img, Point start, Point end );
int main( void ){
  char atom_window[] = "Drawing 1: Atom";
  char rook_window[] = "Drawing 2: Rook";
  Mat atom_image = Mat::zeros( w, w, CV_8UC3 );
  Mat rook_image = Mat::zeros( w, w, CV_8UC3 );
  MyEllipse( atom_image, 90 );
  MyEllipse( atom_image, 0 );
  MyEllipse( atom_image, 45 );
  MyEllipse( atom_image, -45 );
  MyFilledCircle( atom_image, Point( w/2, w/2) );
  MyPolygon( rook_image );
  rectangle( rook_image,
         Point( 0, 7*w/8 ),
         Point( w, w),
         Scalar( 0, 255, 255 ),
         FILLED,
         LINE_8 );
  MyLine( rook_image, Point( 0, 15*w/16 ), Point( w, 15*w/16 ) );
  MyLine( rook_image, Point( w/4, 7*w/8 ), Point( w/4, w ) );
  MyLine( rook_image, Point( w/2, 7*w/8 ), Point( w/2, w ) );
  MyLine( rook_image, Point( 3*w/4, 7*w/8 ), Point( 3*w/4, w ) );
//   imshow( atom_window, atom_image );
  imwrite( "./atom_window.jpg", atom_image );

//   moveWindow( atom_window, 0, 200 );
//   imshow( rook_window, rook_image );
  imwrite( "./rook_window.jpg", rook_image );
//   moveWindow( rook_window, w, 200 );
//   waitKey( 0 );
  return(0);
}
void MyEllipse( Mat img, double angle )
{
  int thickness = 2;
  int lineType = 8;
  ellipse( img,
       Point( w/2, w/2 ),
       Size( w/4, w/16 ),
       angle,
       0,
       360,
       Scalar( 255, 0, 0 ),
       thickness,
       lineType );
}
void MyFilledCircle( Mat img, Point center )
{
  circle( img,
      center,
      w/32,
      Scalar( 0, 0, 255 ),
      FILLED,
      LINE_8 );
}
void MyPolygon( Mat img )
{
  int lineType = LINE_8;
  Point rook_points[1][20];
  rook_points[0][0]  = Point(    w/4,   7*w/8 );
  rook_points[0][1]  = Point(  3*w/4,   7*w/8 );
  rook_points[0][2]  = Point(  3*w/4,  13*w/16 );
  rook_points[0][3]  = Point( 11*w/16, 13*w/16 );
  rook_points[0][4]  = Point( 19*w/32,  3*w/8 );
  rook_points[0][5]  = Point(  3*w/4,   3*w/8 );
  rook_points[0][6]  = Point(  3*w/4,     w/8 );
  rook_points[0][7]  = Point( 26*w/40,    w/8 );
  rook_points[0][8]  = Point( 26*w/40,    w/4 );
  rook_points[0][9]  = Point( 22*w/40,    w/4 );
  rook_points[0][10] = Point( 22*w/40,    w/8 );
  rook_points[0][11] = Point( 18*w/40,    w/8 );
  rook_points[0][12] = Point( 18*w/40,    w/4 );
  rook_points[0][13] = Point( 14*w/40,    w/4 );
  rook_points[0][14] = Point( 14*w/40,    w/8 );
  rook_points[0][15] = Point(    w/4,     w/8 );
  rook_points[0][16] = Point(    w/4,   3*w/8 );
  rook_points[0][17] = Point( 13*w/32,  3*w/8 );
  rook_points[0][18] = Point(  5*w/16, 13*w/16 );
  rook_points[0][19] = Point(    w/4,  13*w/16 );
  const Point* ppt[1] = { rook_points[0] };
  int npt[] = { 20 };
  fillPoly( img,
        ppt,
        npt,
        1,
        Scalar( 255, 255, 255 ),
        lineType );
}
void MyLine( Mat img, Point start, Point end )
{
  int thickness = 2;
  int lineType = LINE_8;
  line( img,
    start,
    end,
    Scalar( 0, 0, 0 ),
    thickness,
    lineType );
}

1.6.1 编译
# 在CMakeList.txt同级目录中
# cmake会创建一个aarch_build文件夹并在里面编译生成文件
cmake -B aarch_build && cd aarch_build
# 不报错直接make
make

在这里插入图片描述

1.6.2 链接RK3588并进行测试
  1. 使用type-c 连接板子和PC机
  2. 安装adb:sudo apt install adb
  3. adb检测设备(有设备号证明可以):adb devices
    (base) hgh@$ adb devices
    List of devices attached
    (base) hgh@$ adb devices
    List of devices attached
    8xxxxxxxxxxxx263 device
  4. 使用adb push传输文件: adb push ./your/path/sourceFIle /rk3588/root/demo_test/target_file
    5.adb测试:adb shell

在这里插入图片描述

# 执行可执行文件
./opencv_test

生成对应的结果图!!在这里插入图片描述
显示结果图为:
在这里插入图片描述

二、OpenCV推理YOLOv5Face进行人脸检测

2.1 下载OpenCV的人脸检测算法权重

百度网盘下载 提取码: 6kjd
参考链接:yolov5-face-landmarks-opencv-v2

2.2检测detect_face.cpp代码

#include <fstream>
#include <sstream>
#include <iostream>
// #include <cstring>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;
using namespace dnn;
using namespace std;

struct Net_config
{
	float confThreshold; // Confidence threshold
	float nmsThreshold;	 // Non-maximum suppression threshold
	float objThreshold;	 // Object Confidence threshold
	string netname;
};

class YOLO
{
public:
	YOLO(Net_config config);
	int detect(Mat &frame);

private:
	const float anchors[3][6] = {{4, 5, 8, 10, 13, 16}, {23, 29, 43, 55, 73, 105}, {146, 217, 231, 300, 335, 433}};
	const float stride[3] = {8.0, 16.0, 32.0};
	const int inpWidth = 640;
	const int inpHeight = 640;
	float confThreshold;
	float nmsThreshold;
	float objThreshold;

	char netname[20];
	Net net;
	void drawPred(float conf, int left, int top, int right, int bottom, Mat &frame, vector<int> landmark);
	void sigmoid(Mat *out, int length);
};

static inline float sigmoid_x(float x)
{
	return static_cast<float>(1.f / (1.f + exp(-x)));
}

YOLO::YOLO(Net_config config)
{
	cout << "Net use " << config.netname << endl;
	this->confThreshold = config.confThreshold;
	this->nmsThreshold = config.nmsThreshold;
	this->objThreshold = config.objThreshold;
	// strcpy_s(this->netname, config.netname.c_str());
	strcpy(this->netname, config.netname.c_str());

	string modelFile = this->netname;
	// modelFile += "-face.onnx";

	this->net = readNet(modelFile);
}

void YOLO::drawPred(float conf, int left, int top, int right, int bottom, Mat &frame, vector<int> landmark) // Draw the predicted bounding box
{
	// Draw a rectangle displaying the bounding box
	rectangle(frame, Point(left, top), Point(right, bottom), Scalar(0, 0, 255), 2);

	// Get the label for the class name and its confidence
	string label = format("%.2f", conf);

	// Display the label at the top of the bounding box
	int baseLine;
	Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
	top = max(top, labelSize.height);
	// rectangle(frame, Point(left, top - int(1.5 * labelSize.height)), Point(left + int(1.5 * labelSize.width), top + baseLine), Scalar(0, 255, 0), FILLED);
	putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 255, 0), 1);

	// // draw featrue points 绘制特征点
	// for (int i = 0; i < 5; i++)
	// {
	// 	circle(frame, Point(landmark[2 * i], landmark[2 * i + 1]), 1, Scalar(0, 255, 0), -1);
	// }
}

void YOLO::sigmoid(Mat *out, int length)
{
	float *pdata = (float *)(out->data);
	int i = 0;
	for (i = 0; i < length; i++)
	{
		pdata[i] = 1.0 / (1 + expf(-pdata[i]));
	}
}

int YOLO::detect(Mat &frame)
{
	Mat blob;
	blobFromImage(frame, blob, 1 / 255.0, Size(this->inpWidth, this->inpHeight), Scalar(0, 0, 0), true, false);
	this->net.setInput(blob);
	vector<Mat> outs;
	this->net.forward(outs, this->net.getUnconnectedOutLayersNames());

	/////generate proposals
	vector<float> confidences;
	vector<Rect> boxes;
	vector<vector<int>> landmarks;
	float ratioh = (float)frame.rows / this->inpHeight, ratiow = (float)frame.cols / this->inpWidth;
	int n = 0, q = 0, i = 0, j = 0, nout = 16, row_ind = 0, k = 0; /// xmin,ymin,xamx,ymax,box_score,x1,y1, ... ,x5,y5,face_score
	for (n = 0; n < 3; n++)										   /// 特征图尺度
	{
		int num_grid_x = (int)(this->inpWidth / this->stride[n]);
		int num_grid_y = (int)(this->inpHeight / this->stride[n]);
		for (q = 0; q < 3; q++) /// anchor
		{
			const float anchor_w = this->anchors[n][q * 2];
			const float anchor_h = this->anchors[n][q * 2 + 1];
			for (i = 0; i < num_grid_y; i++)
			{
				for (j = 0; j < num_grid_x; j++)
				{
					float *pdata = (float *)outs[0].data + row_ind * nout;
					float box_score = sigmoid_x(pdata[4]);
					if (box_score > this->objThreshold)
					{
						float face_score = sigmoid_x(pdata[15]);
						// if (face_score > this->confThreshold)
						//{
						float cx = (sigmoid_x(pdata[0]) * 2.f - 0.5f + j) * this->stride[n]; /// cx
						float cy = (sigmoid_x(pdata[1]) * 2.f - 0.5f + i) * this->stride[n]; /// cy
						float w = powf(sigmoid_x(pdata[2]) * 2.f, 2.f) * anchor_w;			 /// w
						float h = powf(sigmoid_x(pdata[3]) * 2.f, 2.f) * anchor_h;			 /// h

						int left = (cx - 0.5 * w) * ratiow;
						int top = (cy - 0.5 * h) * ratioh;

						confidences.push_back(face_score);
						boxes.push_back(Rect(left, top, (int)(w * ratiow), (int)(h * ratioh)));
						vector<int> landmark(10);
						for (k = 5; k < 15; k += 2)
						{
							const int ind = k - 5;
							landmark[ind] = (int)(pdata[k] * anchor_w + j * this->stride[n]) * ratiow;
							landmark[ind + 1] = (int)(pdata[k + 1] * anchor_h + i * this->stride[n]) * ratioh;
						}
						landmarks.push_back(landmark);
						//}
					}
					row_ind++;
				}
			}
		}
	}

	// Perform non maximum suppression to eliminate redundant overlapping boxes with
	// lower confidences
	vector<int> indices;
	NMSBoxes(boxes, confidences, this->confThreshold, this->nmsThreshold, indices);
	for (size_t i = 0; i < indices.size(); ++i)
	{
		int idx = indices[i];
		Rect box = boxes[idx];
		this->drawPred(confidences[idx], box.x, box.y,
					   box.x + box.width, box.y + box.height, frame, landmarks[idx]);
	}

	return indices.size();
}

int main(int argc, char **argv)
{
  	char*          model_name = NULL;
	
	if (argc != 3)
	{
		cout << argv[0] << " please input images <jpg> "<< endl;
		return -1;
	}
  	model_name       = (char*)argv[1];
	// Net_config yolo_nets = {0.3, 0.5, 0.3, "weights/yolov5s"}; /// choice = [yolov5s, yolov5m, yolov5l]
	Net_config yolo_nets = {0.3, 0.5, 0.3, model_name}; /// choice = [yolov5s, yolov5m, yolov5l]
	YOLO yolo_model(yolo_nets);
	char *imgpath = argv[2];
	cout << "Usage:  " << imgpath << "<rknn model> <jpg> \n";
	Mat srcimg = imread(imgpath);
	int nums = yolo_model.detect(srcimg);
	cout << "检测的人数为: " << nums << endl;
	imwrite("./result_det.jpg", srcimg);

	return 0;
}

2.3同级目录下的CMakeLists.txt文件代码

# 设置cmake版本要求
cmake_minimum_required(VERSION 3.4.1)
# 项目工程名称
project(test_opencv_face)
# 设置编译环境、架构工具  (下面这个设置了,本地的x86架构不能运行)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)

message("CMAKE_SOURCE_DIR is ====> ${CMAKE_SOURCE_DIR}")
set(OpenCV_DIR /home/hgh/ai/opencv/opencv-4.5.5/arch_build/install/share/opencv4)

# 查找默认环境的OpenCV
find_package(OpenCV REQUIRED)

message("OpenCV_DIR is --> ${OpenCV_DIR}")

# # 引用头文件
# include_directories(BEFORE /home/hgh/ai/opencv/opencv-4.5.5/arch_build/install/include/opencv4)
# include_directories(BEFORE /home/hgh/ai/opencv/opencv-4.5.5/arch_build/install/include/opencv4/opencv2)
# # 引用库文件
link_directories(/home/hgh/ai/opencv/opencv-4.5.5/arch_build/install/lib)
message("link_directories is --> ")

# 打印相关信息
message(STATUS "opencv library status: ")
message(STATUS "\t version ${OpenCV_VERSION}")
message(STATUS "\t libraries: ${OpenCV_LIBS}")
message(STATUS "\t include path ${OpenCV_INCLUDE_DIRS}")
message("---------------------------------------")

add_executable(yolo_test 
                detect_face.cpp
)   
# 若上面的 link_directories 指定文件夹中没有对应的链接库,那么会到本地环境搜索对应的库,但是不同架构的so会报错
target_link_libraries(yolo_test 
    ${OpenCV_LIBS}
)

2.4编译源文件并放到rk3588执行

# 在CMakeList.txt同级目录中
# cmake会创建一个aarch_build文件夹并在里面编译生成文件
cmake -B aarch_build && cd aarch_build
# 不报错直接make
make

生成的目录中yolo_test文件
在这里插入图片描述
ubuntu20.04与rk开发板使用filezilla进行文件传输:下载链接

使用electrem进行ssh连接开发板(也可以传输文件,开源好用!):下载链接

上传onnx、人脸检测图、OpenCV的so库到rk3588中
在当前目录lib中放入OpenCV的so库,并配置链接环境库变量(不然会报缺失so库):

export LD_LIBRARY_PATH = ./lib
运行可执行文件,格式./yolo_test model.onnx image.jpg :

# 增加可执行权限
chmod +x yolo_test
# 执行
./yolo_test yolov5s-face.onnx selfie.jpg 

请添加图片描述
图中输出检测结果图result_det.jpg和识别的人脸个数
在这里插入图片描述

注意事项

1.cmake编译引用OpenCV动态库so报错

交叉编译好的OpenCV4.5.5版本,在ubuntu系统中的cmakelists.txt中引用了动态库so。先在CMakeLists.txt文件中按照链接的path路径 link_directories(path),若未找到会查找本地的so库。这样会导致编译aarch64架构文件,却使用了x86编译的so,会导致make报错,报错现象为:

error adding symbols: file in wrong format
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/yolo_test.dir/build.make:104:yolo_test] 错误 1
make[1]: *** [CMakeFiles/Makefile2:7

在这里插入图片描述
解决方法(该解决办法会导致移植到另一部机子依赖库出问题,OpenCVModules.cmake 里面包含了编译的绝对路径):把编译的aarch_build文件夹中生成的*.cmake文件,拷贝到install/share/opencv4 中。make编译链接就不会报错!!!【见下图】
~~在这里插入图片描述

正确打开方式:设置OpenCV_DIR的路径指定为install中的lib/cmake/opencv4路径,后面的find_package(OpenCV REQUIRED)就会搜索到自己编译的OpenCV的install库中。
比如:set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/3rdparty/rk3588_opencv455/lib/cmake/opencv4)
在这里插入图片描述

参考链接:

opencv在 Cmakelist的写法以及编译详解: https://blog.csdn.net/qq_41612863/article/details/122149124
交叉编译aarch64版本opencv-4.5.5: https://blog.csdn.net/heqingchun16/article/details/129054037
ubuntu20.04的vscode编译OpenCV4.5.5: https://blog.csdn.net/xiangfengl/article/details/122945924

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐