评测 香橙派OrangePi在智能交通上的应用

news2024/10/6 22:22:59

1、OrangePi应用场景

关于 Orange Pi AI Pro 开发板是香橙派联合华为精心打造的高性能 AI 开发板,其搭载了昇腾 AI 处理器,可提供 8TOPS INT8 的计算能力,内存提供了 8GB 和 16GB两种版本。可以实现图像、视频等多种数据分析与推理计算,可广泛用于教育、机器人、无人机等场景。此次我将测评板卡在智能交通上的应用。处理四路视觉检测、追踪,且完成多摄像头追踪。通常我们将该结构化数据后叫全息路口。
由于简单的开机,远程以及硬件等测评都较为简单,跑通官方demo即可,此处我们移植比较复杂的算法,将该算力压榨干净,以证明其强大的处理能力。硬件接口图如下

img

1.1 智能交通的全息路口介绍

近年来,国内城镇化迅速发展,城市交通出行需求增长迅速,给城市道路交通秩序与安全管控带来较大压力。路口作为城市道路的重要节点,需要具备精准的数字化感知能力,才能有效提升道路精细化治理能力。

而传统路口的信息感知不精准,交管部门无法根据路口排队车辆多少、有无事故车辆、有无车辆溢出等关键信息来指挥交通。

全息路网通过融合视频和雷达数据,结合高精地图、边缘计算等技术,可全天候精准采集路口车道级流量、排队长度、车辆速度、行车轨迹、停车次数、事故等信息,彻底解决路口绿灯空放问题,可大大缩短了红灯等待时间。某地交管部门在东二环路南北路段应用线性绿波带后。高峰期内,行程时间压缩了20%左右,平峰期行程时间平均压缩了30%,停车次数降低了2次以上。

全息路网也可快速发现路网的异常事件及违法行为,将事件视频、事件地址与辅助判罚报告通知指挥中心,形成接处警快速闭环机制,实现远程处理擦碰事故,有效缓解拥堵,避免二次事故。这里放出一张结果图吧,方便大家了解。使用视觉感知算法,将十字路口所有交通参与者识别、追踪、定位,孪生在高精地图上

img

1.2 Orange Pi AI Pro 开发板与相机连接图

总的来说就是用Orange Pi AI Pro处理四路摄像头数据,将四路摄像头接入交换机,再将Orange Pi AI Pro的网口也接入交换机,这样Orange Pi AI Pro就可以拉到四路相机的视频流了,示意图如下

img

首先我们在十字路口安装四个枪机,照射车辆去向,当然也可以复用交警的电警杆,路口安装示意图如下所示

img

2、Orange Pi AI Pro处理相机rtsp流

由于路口枪机均是网络相机,视觉算法的处理流程如下图所示

img

2.1 Orange Pi AI Pro解析rtsp视频流

用海康威视的摄像实时读取视频,读取视频的格式是YV12格式,摄像机进行压缩算法处理后的H264视频流通过RTSP协议传向网络应用层,用户拿到的数据是H265格式,需要使用ACLLite接口完成视频解码,将H265文件的前十帧并解码为YUV图片硬解码,得到原来的YV12格式的图像视频,最后经过opencv处理得到RGB格式显示到屏幕或者直接使用ACLLite进行颜色空间转换进行预处理。
官方给出了如何将H265编码视频如何解析成H265文件,具体可参考

https://gitee.com/ascend/EdgeAndRobotics/blob/master/Samples/VideoDecode/src/main.cpp
也可以参考这个案例

https://gitee.com/ascend/samples/tree/master/inference/modelInference/sampleResnetRtsp/cpp

但是并未给出如何解析rtsp流到H265的步骤,这里我们使用AclLite库,使用海康rtsp流作为样例的输入数据,使能Acllite解码rtsp,缩放图片。
直接贴代码

#include <dirent.h>
#include <opencv2/opencv.hpp>
#include "AclLiteUtils.h"
#include "AclLiteImageProc.h"
#include "AclLiteVideoProc.h"
#include "AclLiteResource.h"
#include "AclLiteError.h"
#include "AclLiteModel.h"
//#include "label.h"

using namespace std;
using namespace cv;
typedef enum Result {
    SUCCESS = 0,
    FAILED = 1
} Result;

aclrtRunMode runMode_;
ImageData resizedImage_;
int32_t modelWidth_;
int32_t modelHeight_;

Result ProcessInput(string testImgPath)
{
    // read image from file
    ImageData image;
    AclLiteError ret = ReadJpeg(image, testImgPath);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("ReadJpeg failed, errorCode is %d", ret);
        return FAILED;
    }

    // copy image from host to dvpp
    ImageData imageDevice;
    ret = CopyImageToDevice(imageDevice, image, runMode_, MEMORY_DVPP);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("CopyImageToDevice failed, errorCode is %d", ret);
        return FAILED;
    }
    AclLiteImageProc imageProcess_;
    // image decoded from JPEG format to YUV
    ImageData yuvImage;
    ret = imageProcess_.JpegD(yuvImage, imageDevice);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("Convert jpeg to yuv failed, errorCode is %d", ret);
        return FAILED;
    }

    // zoom image to modelWidth_ * modelHeight_
    ret = imageProcess_.Resize(resizedImage_, yuvImage, modelWidth_, modelHeight_);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("Resize image failed, errorCode is %d", ret);
        return FAILED;
    }
    return SUCCESS;
}


int main(int argc, char *argv[]) 
{
    int argNum = 2;
    if ((argc < argNum)) {
        ACLLITE_LOG_ERROR("Please input: ./main rtsp address");
        return FAILED;
    }

    AclLiteError ret = aclrtGetRunMode(&runMode_);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("get runMode failed, errorCode is %d", ret);
        return FAILED;
    }

    std::string inputDataPath =  string(argv[1]);


    if (ret) {
        ACLLITE_LOG_ERROR("Init resource failed, error %d", ret);
        return FAILED;
    }

    // open camera
    int device = 0;
    AclLiteVideoProc cap = AclLiteVideoProc(inputDataPath, device);
    if (!cap.IsOpened()) {
        ACLLITE_LOG_ERROR("Open camera failed");
        return FAILED;
    }
   
    // read frame from camera and inference
    while (true) 
    {
        ImageData image;
        AclLiteError ret = cap.Read(image);
        if (ret) {
            break;
        }
    ACLLITE_LOG_INFO("Execute sample success");
    return SUCCESS;
    }
}

解码后可使用opencv将yuv转成RGB进行屏幕输出如图

img

同时拉四路相机流效果如下:

img

2.2 Orange Pi AI Pro yolo 推理

通过上面解码之后,我们已经得到YUV图,接下来以YOLOV7网络模型,需要使用Acllite对图片进行预处理,并通过模型转换使能静态AIPP功能,使能AIPP功能后,YUV420SP_U8格式图片转化为RGB,然后减均值和归一化操作,并将该信息固化到转换后的离线模型中,对YOLOV7网络执行推理,对图片进行物体检测和分类,并给出标定框和类别置信度。
图片物体检测,并且在图片上给出物体标注框,类别以及置信度。
模型训练与 转换直接参考官网

https://gitee.com/ascend/samples/tree/master/inference/modelInference/sampleYOLOV7

此处我贴出代码与效果


#include <dirent.h>
#include <opencv2/opencv.hpp>
#include "AclLiteUtils.h"
#include "AclLiteImageProc.h"
#include "AclLiteResource.h"
#include "AclLiteError.h"
#include "AclLiteModel.h"
#include "label.h"

using namespace std;
using namespace cv;
typedef enum Result {
    SUCCESS = 0,
    FAILED = 1
} Result;

typedef struct BoundBox {
    float x;
    float y;
    float width;
    float height;
    float score;
    size_t classIndex;
    size_t index;
} BoundBox;

bool sortScore(BoundBox box1, BoundBox box2)
{
    return box1.score > box2.score;
}

class SampleYOLOV7 {
    public:
    SampleYOLOV7(const char *modelPath, const int32_t modelWidth, const int32_t modelHeight);
    Result InitResource();
    Result ProcessInput(string testImgPath);
    Result Inference(std::vector<InferenceOutput>& inferOutputs);
    Result GetResult(std::vector<InferenceOutput>& inferOutputs, string imagePath, size_t imageIndex, bool release);
    ~SampleYOLOV7();
    private:
    void ReleaseResource();
    AclLiteResource aclResource_;
    AclLiteImageProc imageProcess_;
    AclLiteModel model_;
    aclrtRunMode runMode_;
    ImageData resizedImage_;
    const char *modelPath_;
    int32_t modelWidth_;
    int32_t modelHeight_;
};

SampleYOLOV7::SampleYOLOV7(const char *modelPath, const int32_t modelWidth, const int32_t modelHeight) :
                           modelPath_(modelPath), modelWidth_(modelWidth), modelHeight_(modelHeight)
{
}

SampleYOLOV7::~SampleYOLOV7()
{
    ReleaseResource();
}

Result SampleYOLOV7::InitResource()
{
    // init acl resource
    AclLiteError ret = aclResource_.Init();
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("resource init failed, errorCode is %d", ret);
        return FAILED;
    }

    ret = aclrtGetRunMode(&runMode_);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("get runMode failed, errorCode is %d", ret);
        return FAILED;
    }

    // init dvpp resource
    ret = imageProcess_.Init();
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("imageProcess init failed, errorCode is %d", ret);
        return FAILED;
    }

    // load model from file
    ret = model_.Init(modelPath_);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("model init failed, errorCode is %d", ret);
        return FAILED;
    }
    return SUCCESS;
}

Result SampleYOLOV7::ProcessInput(string testImgPath)
{
    // read image from file
    ImageData image;
    AclLiteError ret = ReadJpeg(image, testImgPath);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("ReadJpeg failed, errorCode is %d", ret);
        return FAILED;
    }

    // copy image from host to dvpp
    ImageData imageDevice;
    ret = CopyImageToDevice(imageDevice, image, runMode_, MEMORY_DVPP);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("CopyImageToDevice failed, errorCode is %d", ret);
        return FAILED;
    }

    // image decoded from JPEG format to YUV
    ImageData yuvImage;
    ret = imageProcess_.JpegD(yuvImage, imageDevice);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("Convert jpeg to yuv failed, errorCode is %d", ret);
        return FAILED;
    }

    // zoom image to modelWidth_ * modelHeight_
    ret = imageProcess_.Resize(resizedImage_, yuvImage, modelWidth_, modelHeight_);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("Resize image failed, errorCode is %d", ret);
        return FAILED;
    }
    return SUCCESS;
}

Result SampleYOLOV7::Inference(std::vector<InferenceOutput>& inferOutputs)
{
    // create input data set of model
    AclLiteError ret = model_.CreateInput(static_cast<void *>(resizedImage_.data.get()), resizedImage_.size);
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("CreateInput failed, errorCode is %d", ret);
        return FAILED;
    }

    // inference
    ret = model_.Execute(inferOutputs);
    if (ret != ACL_SUCCESS) {
        ACLLITE_LOG_ERROR("execute model failed, errorCode is %d", ret);
        return FAILED;
    }
    return SUCCESS;
}

Result SampleYOLOV7::GetResult(std::vector<InferenceOutput>& inferOutputs,
                               string imagePath, size_t imageIndex, bool release)
{
    uint32_t outputDataBufId = 0;
    float *classBuff = static_cast<float *>(inferOutputs[outputDataBufId].data.get());
    // confidence threshold
    float confidenceThreshold = 0.25;

    // class number
    size_t classNum = 80;

    // number of (x, y, width, hight, confidence)
    size_t offset = 5;

    // total number = class number + (x, y, width, hight, confidence)
    size_t totalNumber = classNum + offset;

    // total number of boxs
    size_t modelOutputBoxNum = 25200;

    // top 5 indexes correspond (x, y, width, hight, confidence),
    // and 5~85 indexes correspond object's confidence
    size_t startIndex = 5;

    // read source image from file
    cv::Mat srcImage = cv::imread(imagePath);
    int srcWidth = srcImage.cols;
    int srcHeight = srcImage.rows;

    // filter boxes by confidence threshold
    vector <BoundBox> boxes;
    size_t yIndex = 1;
    size_t widthIndex = 2;
    size_t heightIndex = 3;
    size_t classConfidenceIndex = 4;
    for (size_t i = 0; i < modelOutputBoxNum; ++i) {
        float maxValue = 0;
        float maxIndex = 0;
        for (size_t j = startIndex; j < totalNumber; ++j) {
            float value = classBuff[i * totalNumber + j] * classBuff[i * totalNumber + classConfidenceIndex];
                if (value > maxValue) {
                // index of class
                maxIndex = j - startIndex;
                maxValue = value;
            }
        }
        float classConfidence = classBuff[i * totalNumber + classConfidenceIndex];
        if (classConfidence >= confidenceThreshold) {
            // index of object's confidence
            size_t index = i * totalNumber + maxIndex + startIndex;

            // finalConfidence = class confidence * object's confidence
            float finalConfidence =  classConfidence * classBuff[index];
            BoundBox box;
            box.x = classBuff[i * totalNumber] * srcWidth / modelWidth_;
            box.y = classBuff[i * totalNumber + yIndex] * srcHeight / modelHeight_;
            box.width = classBuff[i * totalNumber + widthIndex] * srcWidth/modelWidth_;
            box.height = classBuff[i * totalNumber + heightIndex] * srcHeight / modelHeight_;
            box.score = finalConfidence;
            box.classIndex = maxIndex;
            box.index = i;
            if (maxIndex < classNum) {
                boxes.push_back(box);
            }
        }
           }

    // filter boxes by NMS
    vector <BoundBox> result;
    result.clear();
    float NMSThreshold = 0.45;
    int32_t maxLength = modelWidth_ > modelHeight_ ? modelWidth_ : modelHeight_;
    std::sort(boxes.begin(), boxes.end(), sortScore);
    BoundBox boxMax;
    BoundBox boxCompare;
    while (boxes.size() != 0) {
        size_t index = 1;
        result.push_back(boxes[0]);
        while (boxes.size() > index) {
            boxMax.score = boxes[0].score;
            boxMax.classIndex = boxes[0].classIndex;
            boxMax.index = boxes[0].index;

            // translate point by maxLength * boxes[0].classIndex to
            // avoid bumping into two boxes of different classes
            boxMax.x = boxes[0].x + maxLength * boxes[0].classIndex;
            boxMax.y = boxes[0].y + maxLength * boxes[0].classIndex;
            boxMax.width = boxes[0].width;
            boxMax.height = boxes[0].height;

            boxCompare.score = boxes[index].score;
            boxCompare.classIndex = boxes[index].classIndex;
            boxCompare.index = boxes[index].index;

            // translate point by maxLength * boxes[0].classIndex to
            // avoid bumping into two boxes of different classes
            boxCompare.x = boxes[index].x + boxes[index].classIndex * maxLength;
            boxCompare.y = boxes[index].y + boxes[index].classIndex * maxLength;
            boxCompare.width = boxes[index].width;
            boxCompare.height = boxes[index].height;

            // the overlapping part of the two boxes
            float xLeft = max(boxMax.x, boxCompare.x);
            float yTop = max(boxMax.y, boxCompare.y);
            float xRight = min(boxMax.x + boxMax.width, boxCompare.x + boxCompare.width);
            float yBottom = min(boxMax.y + boxMax.height, boxCompare.y + boxCompare.height);
            float width = max(0.0f, xRight - xLeft);
            float hight = max(0.0f, yBottom - yTop);
            float area = width * hight;
            float iou =  area / (boxMax.width * boxMax.height + boxCompare.width * boxCompare.height - area);

            // filter boxes by NMS threshold
            if (iou > NMSThreshold) {
                boxes.erase(boxes.begin() + index);
                continue;
            }
            ++index;
        }
        boxes.erase(boxes.begin());
    }

    // opencv draw label params
    const double fountScale = 0.5;
    const uint32_t lineSolid = 2;
    const uint32_t labelOffset = 11;
    const cv::Scalar fountColor(0, 0, 255);
    const vector <cv::Scalar> colors{
        cv::Scalar(237, 149, 100), cv::Scalar(0, 215, 255),
        cv::Scalar(50, 205, 50), cv::Scalar(139, 85, 26)};

    int half = 2;
    for (size_t i = 0; i < result.size(); ++i) {
        cv::Point leftUpPoint, rightBottomPoint;
        leftUpPoint.x = result[i].x - result[i].width / half;
        leftUpPoint.y = result[i].y - result[i].height / half;
        rightBottomPoint.x = result[i].x + result[i].width / half;
        rightBottomPoint.y = result[i].y + result[i].height / half;
        cv::rectangle(srcImage, leftUpPoint, rightBottomPoint, colors[i % colors.size()], lineSolid);
        string className = label[result[i].classIndex];
        string markString = to_string(result[i].score) + ":" + className;
        cv::putText(srcImage, markString, cv::Point(leftUpPoint.x, leftUpPoint.y + labelOffset),
                    cv::FONT_HERSHEY_COMPLEX, fountScale, fountColor);
    }
    string savePath = "out_" + to_string(imageIndex) + ".jpg";
    cv::imwrite(savePath, srcImage);
    if (release){
        free(classBuff);
        classBuff = nullptr;
    }
    return SUCCESS;
}

void SampleYOLOV7::ReleaseResource()
{
    model_.DestroyResource();
    imageProcess_.DestroyResource();
    aclResource_.Release();
}

int main()
{
    const char* modelPath = "../model/yolov7x.om";
    const string imagePath = "../data";
    const int32_t modelWidth = 640;
    const int32_t modelHeight = 640;

    // all images in dir
    DIR *dir = opendir(imagePath.c_str());
    if (dir == nullptr)
    {
        ACLLITE_LOG_ERROR("file folder does no exist, please create folder %s", imagePath.c_str());
        return FAILED;
    }
    vector<string> allPath;
    struct dirent *entry;
    while ((entry = readdir(dir)) != nullptr)
    {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0
        || strcmp(entry->d_name, ".keep") == 0)
        {
            continue;
        }else{
            string name = entry->d_name;
            string imgDir = imagePath +"/"+ name;
            allPath.push_back(imgDir);
        }
    }
    closedir(dir);

    if (allPath.size() == 0){
        ACLLITE_LOG_ERROR("the directory is empty, please download image to %s", imagePath.c_str());
        return FAILED;
    }

    // inference
    string fileName;
    bool release = false;
    SampleYOLOV7 sampleYOLO(modelPath, modelWidth, modelHeight);
    Result ret = sampleYOLO.InitResource();
    if (ret == FAILED) {
        ACLLITE_LOG_ERROR("InitResource failed, errorCode is %d", ret);
        return FAILED;
    }

    for (size_t i = 0; i < allPath.size(); i++)
    {
        if (allPath.size() == i){
            release = true;
        }
        std::vector<InferenceOutput> inferOutputs;
        fileName = allPath.at(i).c_str();

        ret = sampleYOLO.ProcessInput(fileName);
        if (ret == FAILED) {
            ACLLITE_LOG_ERROR("ProcessInput image failed, errorCode is %d", ret);
            return FAILED;
        }

        ret = sampleYOLO.Inference(inferOutputs);
        if (ret == FAILED) {
            ACLLITE_LOG_ERROR("Inference failed, errorCode is %d", ret);
            return FAILED;
        }

        ret = sampleYOLO.GetResult(inferOutputs, fileName, i, release);
        if (ret == FAILED) {
            ACLLITE_LOG_ERROR("GetResult failed, errorCode is %d", ret);
            return FAILED;
        }
    }
    return SUCCESS;
}

这里我还加上了车牌识别的功能

img

2.3 Orange Pi AI Pro目标追踪与定位

目标追踪的思路如下:
将检测框按置信度分成两类,高分框与低分框。先用高分框与追踪轨迹进行第一次匹配。第二次匹配使用低分框与未匹配的轨迹进行匹配对于得分高于阈值但未匹配的目标框,为其新建追踪轨迹.追踪代码完全基于Bytetrack且是跑在CPU上,大家直接去官方整合即可,这里介绍下定位
在用OpenCV中的函数,通过4对对应的点的坐标计算两个图像之间单应矩阵H,然后调用射影变换函数,将一幅图像变换到另一幅图像的视角中。当时只是知道通过单应矩阵,能够将图像1中的像素坐标(u1,v1))变换到图像2中对应的位置上(u2,v2)。

单应(Homography)是射影几何中的概念,又称为射影变换。它把一个射影平面上的点(三维齐次矢量)映射到另一个射影平面上,并且把直线映射为直线,具有保线性质。总的来说,单应是关于三维齐次矢量的一种线性变换,可以用一个3x3的非奇异矩阵H表示这是一个齐次坐标的等式,

 x1=H * x2

在这里H是一个3×3的齐次矩阵,具有8个未知量。假设已经取得了两图像之间的单应,则可单应矩阵H可以将两幅图像关联起来。这有了很多实际的应用,例如图像的校正、对齐以及在SLAM中估计两个相机间的运动。此处我用作做为联合标定。首先我们将是地面平面假设为一个二维水平面,忽略其坡度。
即可得
地面平面上点p1(x1,y1),与图像上点p2(x2,y2)是一对匹配得点对,其单应性矩阵为H,则有

img

接下来我们求解这个矩阵
在真实的应用场景中,我们计算的点对中都会包含噪声。比如点的位置偏差几个像素,甚至出现特征点对误匹配的现象,如果只使用4个点对来计算单应矩阵,那会出现很大的误差。因此,为了使得计算更精确,一般都会使用远大于4个点对来计算单应矩阵。另外上述方程组采用直接线性解法通常很难得到最优解,所以实际使用中一般会用其他优化方法,如奇异值分解、Levenberg-Marquarat(LM)算法等进行求解。

cv::Mat oHomoMat_inv= cv::findHomography(m_vo2dPt, m_vo3dPt, 0, 0);

本工程里使用了四种方法,分别为直接解方程组,最小二乘法,ransac方法,基于PROSAC的鲁棒算法,具体解法可自行百度。标定之后的结果以及换算结果如图

img

3、多相机追踪

(a)融合单个杆件上相机与枪机的检测结果
首先需要将单个相机单元的结果进行初步筛选整合,虽然鱼眼的作用是对枪机进行补盲,但不能直接对两者的结果进行简单相加,因为可能会有由于处于鱼眼和枪机的重叠区域而被重复检测的目标,因此需要剔除可能存在的重复检测结果:

选择遍历枪机的检测结果,按照GPS的距离的每个目标的进行比,找出可能重复识别的目标, 之后将这些初步选出的枪机检测结果的GPS坐标使用相机的单应性矩阵映射到图像坐标系上,此时枪机的GPS坐标变为鱼眼图像上的一个像素点,再与检测框对比计算IOU,经过该轮比对,如IOU超过阈值,能够关联成功即可视为一个重复检测的目标,在这里删除重复检测在枪机下的结果,再将去除后的结果进行合并,即为一个相机单元的检测结果,包括每个目标的类型,在图像坐标的位置(可能是枪机或鱼眼),在GPS坐标的位置,速度,航向角等信息。
(b) 目标三级关联算法
在本步骤中,为了保证重合区域能够关联匹配,首次提出目标三级关联算法完成多杆件目标去重,以及目标跨杆件仍能被追踪。具体实施如下:根据步骤1-5得到各路相机单元的结构化数据,目标ID,目标速度,目标位置、目标航向角、目标车牌,目标时间戳等。
(1)首先分别接收多路相机单元数据,存在独立相机单元的缓冲区,单个相机单元的数据由1-5步骤生成,得到单个目标重要属性attrobj
其中id表示为目标单杆件唯一ID,lat, lon为目标的经纬度,heading为目标与正北方向夹角,len, width, height为目标的长宽高,timestamp为目标出现的时间戳,x,y表示在该相机下的像素坐标。同时该杆件相机数据由多个目标组成,且有个 发送时间戳TimeStamp与杆件名称deviceID,单个杆件数据组成为

(2)多相机追踪根据单杆件相机发送的数据perceptroni 中的目标时间戳timestamp的先后顺序初始化多杆件追踪目标列表tracker,即按照timestamp的顺序对perceptron集合进行排序,将最早出现的perceptron所有目标作为路口多杆件全息目标的集合,记为tracker,且赋予该目标一个全局ID。

全局ID是用来表示该目标在整个路口的唯一性,attri表示该目标的位置相关属性,deviceID则表示该目标由某一个杆件摄像机创建而来。
(3) a) 将接收到的其他杆件相机数据依次按照deviceID,从整个路口目标列表里查询当前目标ID上一帧由哪一个单相机列表维护,若查询到则依然由那个相机进行属性维护,即若新收到的perceptron数据中deviceID相同,则直接对比attr中的id是否相同,若相同则直接更新目标的位置,时间戳等信息; 使用单个相机ID预关联的方案,可以大大降低计算量,节省资源 b)若不同,则将该相机下percptroni的该目标的经纬度(lat,long)通过H矩阵的逆重投影到当前tracker维护的列表目标所在相机的图像坐标系中得到pixel(x,y)进行一一计算1-IOU。

c)关联的相机的图像坐标进行Dioumn = 1-IOU交并比关联,即将perceptroni中的目标属性atti(lat,lon)通过计算转换为单相机下的图像坐标x,y进行计算交并比,其中转换后的图像坐标宽高使用tracker关联目标的宽高。 d)若步骤仍关联不上, 则构建一个代价矩阵cost_matrix,使用距离关联,首先构建一个M x N的全为0的矩阵,其中M为tracker中目标的个数,N为上一步iou未关联上的单杆件perceptron目标个数。
距离计算方法为:

其中R为地球半径,(lat1,lon1) 为tracker目标经纬度, (lat2,lon2) 为未关联的单杆件目标经纬度。接下里根据距离构建代价矩阵cos_matrix

使用匈牙利匹配算法对关联矩阵进行匹配,完成所有目标的关联。
(4)将所有关联上的相机属性进行更新整个路口的目标追踪列表,即使用perceptron中的目标属性更新tracker目标中的属性,从而得到整个路口的目标数据tracker,为了进行下一帧关联准确度更高,保证空间一致性,这里对所有tracker中的经纬度根据当前速度进行预测,使用匀速直线运动模型对所有目标经纬度进行预测。
目标纬度预测:

目标经度预测:

其中arc为地球的平均半径,v为当前tracker目标速度,T为关联时间戳之差。

(4)整合路口目标列表,由于目标各个相机中目标视野不一致,所得到的位置有区别,考虑到相机和目标距离越远,产生误差的可能性越大,采用均值滤波对tracker中的目标位置数据进行处理不够稳健,由于距离目标过远的相机产生的误差会造成平均值的位置产生偏差,因此我们采取加权平均,首先获取到路口目标列表内每个目标的距离每个杆件单元的距离,直接根据目标经纬度以及杆件上的摄像机安装经纬度便可得到,假设目标距离四个杆件的距离分别为,则在加权平均时的权重分别为对距离进行归一化的结果,即

进而求出结合了四个杆件信息获得的最终坐标,形成完整路侧感知数据。

img

由于篇幅有限,后续我再将展开续更。。。。。。。。。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1709454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【记录】网络|没有路由器没有网线,分别使用手机或Windows电脑共享网络给ARM64开发板,应急连接

事情是这样的&#xff0c;我的开发板明明已经选择了记住热点 WiFi 密码&#xff0c;但是却没有在开机的时候自动连接&#xff0c;我又没有放显示器在身边&#xff0c;又不想为了这点事去找个显示器来&#xff0c;就非常难受。 我手边有的设备是&#xff1a; 笔记本电脑&#…

nginx 安全配置

1、前言 前后端分离后&#xff0c;nginx 作为跨域转发工具在日常应用中越来越广泛&#xff0c;它的安全性不能不能忽略。 2、nginx 安装相关说明 2.1 直接下载安装包 在nginx官网下载编译好的安装包&#xff0c;链接地址为nginx: download。如果是linux系统&#xff0c;直接使…

基于FPGA实现LED的闪烁——HLS

基于FPGA实现LED的闪烁——HLS 引言&#xff1a; ​ 随着电子技术的飞速发展&#xff0c;硬件设计和开发的速度与效率成为了衡量一个项目成功与否的关键因素。在传统的硬件开发流程中&#xff0c;工程师通常需要使用VHDL或Verilog等硬件描述语言来编写底层的硬件逻辑&#xff0…

Unity Apple Vision Pro 开发(一):开发前期准备【软硬件要求 | 开发者模式 | 无线调试打包】

文章目录 &#x1f4d5;教程说明&#x1f4d5;硬件要求&#x1f4d5;软件要求⭐Xcode 15.2 及以上⭐visionOS 1.0 (21N301) SDK 或者更高版本⭐Unity 2022 LTS for Apple Silicon (2022.3.18f1及以上的版本)⭐Unity Pro/Unity Enterprise/Unity Industry的授权许可证 &#x1f…

solidwork3D草图案例-曲管

单位mm 3D草图 点击线&#xff0c;根据三视图&#xff0c;绘制直线&#xff0c; 圆角 半径25mm 扫描 三视图 如果觉得好的话&#xff0c;或者有疑问&#xff0c;请关注微信公众号咨询

淘宝扭蛋机小程序开发:探索无限惊喜的购物新体验

随着科技的不断进步&#xff0c;人们的生活方式也在发生翻天覆地的变化。在这个数字化、智能化的时代&#xff0c;淘宝扭蛋机小程序应运而生&#xff0c;为消费者带来了一种全新的购物体验。 淘宝扭蛋机小程序是一款集娱乐、互动、购物于一体的创新应用。它巧妙地将扭蛋机的乐…

JavaWeb开发 3.Web开发 Web前端开发 ③ HTML、CSS

没有一朵花&#xff0c;一开始就是一朵花 —— 24.5.28 HTML、CSS知识在博主前端专栏&#xff0c;可以对照博客大致进行了解 https://blog.csdn.net/m0_73983707/category_12654678.htmlhttps://blog.csdn.net/m0_73983707/category_12654678.html

SpringAdminClient如何将Httpbasic账号密码告知SpringAdminServer

场景&#xff0c;因为Config Service开了权限校验&#xff0c;注册到eureka之后&#xff0c;SpringAdmin查看信息会报错401&#xff0c;如果想在SpringAdmin中正确的看到Config Service的actuator信息则需要将账号密码告知给SpringAdmin&#xff0c;磁力用的是Eureka作为发现服…

java多线程创建方式

1. 继承Thread类 这种方式是通过创建一个新的类继承自Thread类&#xff0c;并覆盖run()方法来创建线程。然后通过创建这个类的对象并调用其start()方法来启动线程。 public class MyThread extends Thread { public void run() { // 在这里定义线程的执行逻辑 …

20240528解决飞凌的OK3588-C的核心板可以刷机不能连接ADB的问题

20240528解决飞凌的OK3588-C的核心板可以刷机不能连接ADB的问题 2024/5/28 16:34 OS&#xff1a;Linux R4/Buildroot 硬件接了3条线出来&#xff0c;一直可以刷机&#xff0c;但是链接ADB异常。 【总是链接不上】 Z:\OK3588_Linux_fs\kernel\arch\arm64\boot\dts\rockchip\OK3…

RuoYI框架集成Sqlite与Mybatis-plus

一、RuoYi 中集成 SQLite 、MyBatis-Plus RuoYi 是一个基于 Spring Boot 的权限管理系统,它默认使用 MySQL 作为数据库。如果你想在 RuoYi 中集成 SQLite 数据库,并使用 MyBatis-Plus 作为 ORM 框架,你需要进行一些配置和代码更改。以下是集成的基本步骤: 添加依赖:在项目…

【算法】位运算算法——两整数之和

题解&#xff1a;两整数之和(位运算算法) 目录 1.题目2.位运算算法3.参考代码4.总结 1.题目 题目链接&#xff1a;LINK 2.位运算算法 这个题目难点就在于不能用、- 那什么能够代替加号呢&#xff1f; 既然数的层面不能用号&#xff0c;那二进制的角度去用号即可。 恰好&a…

【RabbitMQ】SpringAMQP--消息转换器

SpringAMQP–消息转换器 测试发送Object类型消息 1.声明队列 Configuration public class FanoutConfig {Beanpublic Queue objectQueue(){return new Queue("object.queue");} }运行消费者后&#xff1a; 2.发送消息 RunWith(SpringRunner.class) SpringBootTes…

预训练模型语义相似性计算(十一) - M3E和BGE

M3E m3e由MokaAI 训练&#xff0c;开源和评测。 m3e的详细介绍可以看官方的github介绍。本文简要摘录其中一些点&#xff0c;以便后续的应用。 1.千万级 (2200w) 的中文句对数据(开源)。 2.支持同质相似句计算(s2s)和异质检索(s2p)&#xff0c;后续支持代码检索。 3.m3e基座模…

景源畅信:抖音小店新手小白如何做好运营?

在数字时代的浪潮中&#xff0c;抖音小店成为了众多创业者和商家的新宠。但面对激烈的市场竞争和不断变化的平台规则&#xff0c;新手小白如何才能在抖音小店的海洋里稳健航行&#xff0c;捕捉到属于自己的商机呢?接下来的内容将为你揭晓答案。 一、精准定位&#xff0c;明确目…

20240521在Ubuntu20.04下编译RK3588平台的IPC方案

20240521在Ubuntu20.04下编译RK3588平台的IPC方案 2024/5/21 15:27 viewproviewpro-ThinkBook-16-G5-IRH:~$ viewproviewpro-ThinkBook-16-G5-IRH:~$ md5sum RK3588_IPC_SDK.tar.gz 7481cc8d59f697a5fa4fd655de866707 RK3588_IPC_SDK.tar.gz viewproviewpro-ThinkBook-16-G5…

三维科技云展厅如何突破传统展览的局限,赋能企业高效展示

一、三维科技展厅如何实现科技展示 1、高度互动的展示方式 三维科技展厅通过虚拟现实和3D建模技术&#xff0c;为观众提供高度互动的展示体验。观众可以身临其境地参观展品&#xff0c;从而获得更为真实的感受。这样的展示方式不仅能够吸引观众的注意力&#xff0c;还能够提高他…

微服务架构五大设计模式详解,助你领跑行业

微服务架构设计模式详解(5种主流模式) 微服务架构 微服务&#xff0c;一种革命性的架构模式&#xff0c;主张将大型应用分解为若干小服务&#xff0c;通过轻量级通信机制互联。每个服务专注特定业务&#xff0c;具备独立部署能力&#xff0c;轻松融入生产环境&#xff0c;为系…

据库管理-第195期 Oracle RDMA(20240527)

数据库管理195期 2024-05-27 数据库管理-第195期 Oracle & RDMA&#xff08;20240527&#xff09;1 RDMA & Exadata2 RDMA & OCI3 RDMA的难点总结 数据库管理-第195期 Oracle & RDMA&#xff08;20240527&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08…

平方回文数-第13届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第73讲。 平方回文数&#…