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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

12-常用类

1. 包装类 针对八种基本数据类型封装的相应的引用类型。 有了类的特点&#xff0c;就可以调用类中的方法。&#xff08;为什么要封装&#xff09; 基本数据类型包装类booleanBooleanchar CharacterbyteByteshortShortintIntegerlongLongfloatFloatdoubleDouble 1.1 …

seRsync + Rsync 实时同步

1&#xff0c;结构图 2&#xff0c;节点A 2.1 安装rsync yum install -y rsync2.2 安装seRsync 下载这个压缩包sersync2.5.4_64bit_binary_stable_final.tar.gz 解压后&#xff0c;将sersync2复制到系统可执行程序路径&#xff1a;/usr/local/bin/&#xff1b;创建sersync配…

Visual Studio 的使用

目录 1. 引言 2. 安装和配置 2.1 系统要求 2.2 安装步骤 2.3 初次配置 3. 界面介绍 3.1 菜单栏和工具栏 3.2 解决方案资源管理器 3.3 编辑器窗口 3.4 输出窗口 3.5 错误列表 3.6 属性窗口 4. 项目管理 4.1 创建新项目 4.2 导入现有项目 4.3 项目属性配置 5. 代…

SpringSecurity6从入门到实战之SpringSecurity快速入门

SpringSecurity6从入门到实战之SpringSecurity快速入门 环境准备 依赖版本号springsecurity6.0.8springboot3.0.12JDK17 这里尽量与我依赖一致,免得在学习过程中出现位置的bug等 创建工程 这里直接选择springboot初始化快速搭建工程,导入对应的jdk17进行创建 直接勾选一个web…

QtCreator调试运行工程报错,无法找到相关库的的解决方案

最新在使用国产化平台做qt应用开发时&#xff0c;总是遇到qtcreator内调试运行 找不到动态库的问题&#xff0c;为什么会出现这种问题呢&#xff1f;明明编译的时候能够正常通过&#xff0c;运行或者调试的时候找不到相关的库呢&#xff1f;先说结论&#xff0c;排除库本身的问…

计算机网络7——网络安全1 概述与加密

文章目录 一、网络安全问题概述1、计算机网络面临的安全性威胁2、安全的计算机网络3、数据加密模型 二、两类密码体制1、对称密钥密码体制2、公钥密码体制 随着计算机网络的发展&#xff0c;网络中的安全问题也日趋严重。当网络的用户来自社会各个阶层与部门时&#xff0c;大量…

Raven2掠夺者2渡鸦2账号怎么验证 注册怎么验证账号教程

《渡鸦2》作为韩国孕育的次世代MMORPG手游巨制&#xff0c;是《Raven》系列辉煌传奇的最新篇章&#xff0c;它在暗黑奇幻的广袤天地间再度挥洒创意&#xff0c;深度融合前所未有的游戏机制与海量新颖内容&#xff0c;为该类型游戏树立了崭新的里程碑。公测日期锁定在2024年5月2…

2024 在Pycharm管理数据库

2024 在Pycharm管理数据库 Pycharm 社区版DataBase Navigator 数据库管理插件(Plugins)安装使用(sqlite为例添加数据) 文章目录 2024 在Pycharm管理数据库一、Pycharm数据库配置1、Database Navigator插件安装2、连接数据库 二、数据库使用1、插件自带基本操作2、控制台操作 …

LAMP集群分布式实验报告

前景&#xff1a; 1.技术成熟度和稳定性&#xff1a; LAMP架构&#xff08;Linux、Apache、MySQL、PHP&#xff09;自1998年提出以来&#xff0c;经过长时间的发展和完善&#xff0c;已经成为非常成熟和稳定的Web开发平台。其中&#xff0c;Linux操作系统因其高度的灵活性和稳…

恢复视频3个攻略:从不同情况下的恢复方法到实践!

随着科技的进步&#xff0c;我们的生活被各种各样的数字内容所包围&#xff0c;其中&#xff0c;视频因其独特的记录性质&#xff0c;承载着许多重要的资料。但不管是自媒体人还是普通人日常生活随手一拍&#xff0c;都会遇到误删视频的情况。为了帮助您找回手机视频&#xff0…

2024全新升级版家政服务小程序源码 支持家政预约+上门服务+SAAS系统+可二开

随着科技的飞速发展&#xff0c;家政服务行业也迎来了数字化转型的浪潮。为了满足市场日益增长的需求&#xff0c;分享一款2024全新升级版的家政服务小程序源码。该源码不仅支持家政预约和上门服务&#xff0c;还集成了SAAS系统&#xff0c;并支持二次开发&#xff0c;为用户带…

【机器学习300问】103、简单的经典卷积神经网络结构设计成什么样?以LeNet-5为例说明。

一个简单的经典CNN网络结构由&#xff1a;输入层、卷积层、池化层、全连接层和输出层&#xff0c;这五种神经网络层结构组成。它最最经典的实例是LeNet-5&#xff0c;它最早被设计用于手写数字识别任务&#xff0c;包含两个卷积层、两个池化层、几个全连接层&#xff0c;以及最…

企业级OV SSL证书的应用场景和加密手段

为了保护数据传输的安全性与用户隐私&#xff0c;企业级OVSSL&#xff08;Organization Validation SSL&#xff09;证书成为众多企业的首选安全解决方案。本文将深入探讨OVSSL证书的应用场景及其实现数据加密的核心手段&#xff0c;为企业构建坚不可摧的在线信任桥梁提供指南。…

【面试干货】约瑟夫问题

【面试干货】约瑟夫问题 1、实现思想2、代码实现 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 约瑟夫问题 是一个经典的数学问题&#xff0c;描述如下&#xff1a;编号为1, 2, …, n的n个人按顺时针方向围坐一圈&#xff0c;从第1个人开始…

实战教程:使用Go的net/http/fcgi包打造高性能Web应用

实战教程&#xff1a;使用Go的net/http/fcgi包打造高性能Web应用 简介理解FCGI协议FastCGI工作原理CGI与FastCGI对比其他接口对比 初步使用net/http/fcgi包设置和配置基础环境一个简单的FastCGI应用示例本地测试与调试 深入net/http/fcgi包的高级用法探索net/http/fcgi的主要功…

《最新出炉》系列入门篇-Python+Playwright自动化测试-46-鼠标滚轮操作

宏哥微信粉丝群&#xff1a;https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 有些网站为了节省流量和资源&#xff0c;提高加载效率&#xff0c;采用的是动态加载&#xff08;懒加载&#xff09;的&#xff0c;也就是当拖动页面右侧滚动条后会自动加载网…

go语言方法之方法声明

从我们的理解来讲&#xff0c;一个对象其实也就是一个简单的赋值或者一个变量&#xff0c;在这个对象中会包含一些方法&#xff0c;而一个方法则是一个一个和特殊类型关联的函数。一个面向对象的程序会用方法来表达其属性和对应的操作&#xff0c;这样使用这个对象的用户就不需…

云计算-Lambda事件 (Lambda Events)

检索事件信息 (Retrieving Event Information) 在上一个主题中&#xff0c;我们已经看到了如何创建一个Lambda函数、添加handler、添加触发器和配置执行策略。在本主题中&#xff0c;我们将对其进行扩展。到目前为止&#xff0c;我们看到的handler应用非常简单&#xff0c;但我…

STM32_HAL_使用FPEC实现闪存的读写

STM32的FLASH结构 主存储器&#xff08;Main Memory&#xff09;&#xff1a;这是STM32中最大的存储区域&#xff0c;用于存储用户的程序代码、常量数据以及程序运行时不变的数据。STM32的主存储器通常被组织为多个扇区&#xff08;sector&#xff09;&#xff0c;每个扇区的大…

项目十三:搜狗——python爬虫实战案例

根据文章项目十二&#xff1a;简单的python基础爬虫训练-CSDN博客的简单应用&#xff0c;这一次来升级我们的技术&#xff0c;那么继续往下看&#xff0c;希望对技术有好运。 还是老样子&#xff0c;按流程走&#xff0c;一条龙服务&#xff0c;嘿嘿。 第一步&#xff1a;导入…