在rockchip官方的例程https://github.com/airockchip/rknn_model_zoo/tree/main/examples/yolov5中有最新的rknn格式运行yolov5的例程。本文是基于用多线程yolov5的demohttps://github.com/leafqycc/rknn-cpp-Multithreading运行的。
在复现代码后,YOLOv5的检测结果经后处理代码的输出格式依次为:
name-检测类别名
box.left-检测框左上角的x坐标
box.top-检测框左上角的y坐标
box.right-检测框右下角的x坐标
box.bottom-检测框右下角的y坐标
prop-检测框的置信度
因为实际应用需要将识别结果的图片坐标转换为实际空间中的二维坐标,因此本文采用九点标定法对检测结果再进行一步处理。九点标定法的定义不赘述了,大致标定步骤为:
①在图片中标记9个点,分布尽量均匀,将它们在图片中的坐标存起来
②通过实际测量得到图中九个点在实际空间中的二维坐标,并将这就个坐标也存起来
③建立一个映射矩阵拟合这九个点的映射关系
④当检测结果为图片中的某一坐标时,将坐标输入到映射矩阵中,得到输出的实际空间坐标
如图,这是一张640×640的图片,图中均匀的分布有九个点,我通过python实现一个简单的九点标定法坐标转换
import numpy as np
import cv2
# 定义图像中的九个圆的像素坐标 (假设这些坐标已知)
pixel_coords = np.array([
[160, 160], [320, 160], [480, 160],
[160, 320], [320, 320], [480, 320],
[160, 480], [320, 480], [480, 480]
], dtype=np.float32)
# 假设实际空间中的九个圆的坐标 (单位: cm)
actual_coords = np.array([
[-20, 40], [0, 40], [20, 40],
[-20, 20], [0, 20], [20, 20],
[-20, 0], [0, 0], [20, 0]
], dtype=np.float32)
# 计算映射矩阵
transform_matrix, _ = cv2.findHomography(pixel_coords, actual_coords)
# 定义一个函数将图像中的任意点转换为实际空间坐标
def pixel_to_actual(pixel_point, transform_matrix):
pixel_point = np.array([pixel_point], dtype=np.float32)
pixel_point = np.array([pixel_point])
actual_point = cv2.perspectiveTransform(pixel_point, transform_matrix)
return actual_point[0][0]
# 选择一个任意点的像素坐标 (例如: [400, 300])
random_pixel = [400, 300]
actual_point = pixel_to_actual(random_pixel, transform_matrix)
print(f"Pixel coordinates: {random_pixel} -> Actual coordinates: {actual_point}")
cv2.findHomography() 是 OpenCV 中的一个函数,用于找到两个图像之间的单应性矩阵(Homography matrix)。在计算机视觉中,单应性矩阵是一个3x3的矩阵,它描述了两个平面之间的相对位置关系。运行代码即可以得到映射矩阵transform_matrix,再将预设的坐标点(400,300)输入到映射矩阵中,就能得到转换结果。
Pixel coordinates: [400, 300] -> Actual coordinates: [10. 22.5]
再将这一部分代码移植到YOLOv5的C程序检测代码中,首先定义标定用的图片坐标集和实际坐标集以及映射矩阵。
std::vector<cv::Point2f> srcPoints;
std::vector<cv::Point2f> dstPoints;
cv::Mat homography;
我的输入输入图片大小为1024×1024,在这个基础上对坐标集初始化。
srcPoints = {
cv::Point2f(256, 256), cv::Point2f(512, 256), cv::Point2f(768, 256),
cv::Point2f(256, 512), cv::Point2f(512, 512), cv::Point2f(768, 512),
cv::Point2f(256, 768), cv::Point2f(512, 768), cv::Point2f(768, 768)
};
dstPoints = {
cv::Point2f(0, 0), cv::Point2f(20, 0), cv::Point2f(40, 0),
cv::Point2f(0, 20), cv::Point2f(20, 20), cv::Point2f(40, 20),
cv::Point2f(0, 40), cv::Point2f(20, 40), cv::Point2f(40, 40)
};
首先包含头文件"opencv2/calib3d.hpp",调用findHomography计算单应性矩阵,在OpenCV中,调用findHomography()方法时指定cv2.RANSAC参数。
homography = cv::findHomography(srcPoints, dstPoints, cv::RANSAC, 3);
if (homography.empty()) {
std::cout << "Error: Homography calculation failed." << std::endl;
return -1;
}
将得到的映射矩阵应用到检测结果的转换中。cv::perspectiveTransform()函数是OpenCV库中用于执行透视变换的函数。它可以将一个坐标点转换为另一个坐标系中的对应点。
int x1 = det_result->box.left;
int y1 = det_result->box.top;
int x2 = det_result->box.right;
int y2 = det_result->box.bottom;
int center_x = (x1 + x2) / 2;
int center_y = (y1 + y2) / 2;
//************坐标转换******************
cv::Point2f imagePoint(center_x, center_y);
std::vector<cv::Point2f> imagePoints = {imagePoint};
std::vector<cv::Point2f> worldPoints;
cv::perspectiveTransform(imagePoints, worldPoints, homography);
sprintf(text_actxy, "Image coordinates: (%d, %d) *** Actual coordinates: (%.2f, %.2f)", center_x, center_y, worldPoints[0].x, worldPoints[0].y);
//************坐标转换******************
//在中心点画一个标记点
circle(orig_img, cv::Point(center_x, center_y), 5, cv::Scalar(0, 0, 255), cv::FILLED);
putText(orig_img, text_actxy, cv::Point(20, 20), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(255, 255, 255));