zoukankan      html  css  js  c++  java
  • OpenCV绘制检测结果

    OpenCV绘制检测结果

    一、介绍

    由于在验证阶段,使用FPGA时我们的算法检测速度很慢,没法直接在主流上进行绘图,否则的话,主流就要等待算法很久才能出图。所以,我们的解决方案是把框推到客户端上,在客户端上进行绘图。

    这时,客户端不仅收到图像帧,音频帧,还会收到一个框信息,需要把三者进行同步显示,不能图像、音频、框不匹配。而图像、音频都是通过ffmpeg写入的,不会有问题,而检测算法这边是独立于前面的出图进程,没有通过ffmpeg打包,所以需要使用ntp时间进行同步。

    问题:

    1. 如果做同步的话,最好能将rtcp_ntp_timestamp设为av_buffer的时间戳,这样同步会准一些,否则两者时间稍有差别。
    2. 如何从ffmpeg取得ntp_time,修改ffmpeg在AVPacket中增加first_rtcp_ntp_time字段(实际上在AVFormatContext中有start_real_time字段,但有些要注意的)
    3. 20fps时,AVPackate->pts中两帧之间的差是:50223us,也就是多了223us。

    二、FFMPEG

    ffmpeg的时间戳介绍:
    https://www.cnblogs.com/gr-nick/p/10993363.html

    由于ffmpeg把一切都封起来了,刚开始的时候发现只能得到pts,而pts的结果是通过计算得到的,它是相对第1帧的相对时间的,而框是绝对时间,无法进行两者的匹配。

    打算在AVPacket中添加一个first_rtcp_ntp_time,这样便可以通过与AVPacket中的pts合并计算得到真实的ntp时间。

    分析代码,发现pts是在libavofrmat/rtpdec.c finalize_packet函数中计算赋值的,同理,我们也在这个函数计算first_rtcp_ntp_time

        pkt->first_rtcp_ntp_time = av_rescale(s->first_rtcp_ntp_time,
                s->st->time_base.den,
                (uint64_t) s->st->time_base.num << 32);
    

    写个程序测试,打印该值,并没有写入到AVPacket中,发现,实际上ffmpeg中内部处理时会新建临时packet对象,而在赋值的时候,之前的代码并没有进行这个字段的拷贝,所以导致值没有传出来,需要修改libavformat/utils.cparse_packet函数:

            out_pkt.stream_index = st->index;
            out_pkt.pts          = st->parser->pts;
            out_pkt.dts          = st->parser->dts;
            out_pkt.pos          = st->parser->pos;
            /* 新增如下行 */
            out_pkt.first_rtcp_ntp_time = pkt->first_rtcp_ntp_time; 
    

    实际上,后来突然发现在AVFormatContext中存在一个start_time_realtime字段,里面存储了ntp时间,所以可以直接利用这个值,不需要修改AVPacket。但存在一个问题,这边在计算时,对这个时间减去了一个NTP_OFFSET(1900~1970的时间差),而我们在编码时并没有加上该偏移,这边相当于多减了NTP_OFFSET,所以使用的时候需要把这个值再加回来。

    s->start_time_realtime = av_rescale (rtpctx->first_rtcp_ntp_time - (NTP_OFFSET << 32), 1000000, 1LL << 32);
    

    三、OpenCV

    OpenCV中包含了一个cap.get(CV_CAP_PROP_POS_MSEC)函数用于获取当前帧时间,其实现方式如下,主要是通过内部维护的frame_number与fps进行计算,但好像对我们没什么用:

    case CV_FFMPEG_CAP_PROP_POS_MSEC:
        return 1000.0*(double)frame_number/get_fps();
    

    OpenCV调用ffmpeg进行rtsp解析的主要流程,最终还是调用av_read_frame()函数:

    OpenCV
    OpenCV

    在VideoCapture中提供一个成员函数cap.getRealTimestamp()用于返回时间戳,
    CvCapture_FFMPEG中提供一个timestamp成员用来保存时间戳,提供一个接口函数,如下:

    @@ -423,6 +423,7 @@ struct CvCapture_FFMPEG
         double  get_duration_sec() const;
         double  get_fps() const;
         int     get_bitrate() const;
    +    int64_t getRealTimestamp() const { return timestamp; }
         AVRational get_sample_aspect_ratio(AVStream *stream) const;
         
    @@ -436,6 +437,7 @@ struct CvCapture_FFMPEG
         AVFrame         * picture;
         AVFrame           rgb_picture;
         int64_t           picture_pts;
    +    int64_t           timestamp;
    

    最后在CvCapture_FFMPEG grabFrame函数中计算时间戳:

    int64_t time = packet.first_rtcp_ntp_time + packet.pts;
    AVRational in_base = {1, 90000};
    AVRational out_base = {1, 1000000};
    timestamp = av_rescale_q(time, in_base, out_base);
    

    如果是使用start_time_realtime则有:

    #define NTP_OFFSET 2208988800ULL            
    int64_t time = av_rescale (ic->start_time_realtime, 1LL << 32, 1000000) + (NTP_OFFSET << 32);
    timestamp = av_rescale (time, 1000000, 1LL << 32) + av_rescale (packet.pts, 1000000, 90000);
    

    四、绘制

    新建一个线程使用libcurl获取websocket传过来的框信息,放入到一个std::list容器中进行管理。

    分别获取帧时间与框时间进行比较,满足条件则绘图:

    list<struct box_result>::iterator it;
    
    /* frame time */
    int64_t frame_time = cap.getRealTimestamp();
    
    /* compare and draw */
    pthread_mutex_lock(&mutex);
    for (it = l.begin(); it != l.end();) {
        int64_t diff = it->timestamp - frame_time;
        if (diff < DROP_TIME * 1.3) {
            it = l.erase(it);
        } else if (diff < -WAIT_TIME*0.1) {
            for (int i = 0; i < it->box_num; ++i)
                rectangle(frame, it->box[i].start, it->box[i].end, it->box[i].color, 2);
            it++;
        } else
            break;
    
    }
    pthread_mutex_unlock(&mutex);
    
    

    五、长时间之后pts不对

    通过打印发现,ffmpeg中计算的地方结果是正确的,但opencv取到的结果确是错误的。

    查看代码,发现有进行wrap操作:

        pkt->dts = wrap_timestamp(st, pkt->dts);
        pkt->pts = wrap_timestamp(st, pkt->pts);
    

    经查文档发现,可以在打开流时,将correct_ts_overflow关掉就可以解决这个问题了。

  • 相关阅读:
    数学+高精度 ZOJ 2313 Chinese Girls' Amusement
    最短路(Bellman_Ford) POJ 1860 Currency Exchange
    贪心 Gym 100502E Opening Ceremony
    概率 Gym 100502D Dice Game
    判断 Gym 100502K Train Passengers
    BFS POJ 3278 Catch That Cow
    DFS POJ 2362 Square
    DFS ZOJ 1002/HDOJ 1045 Fire Net
    组合数学(全排列)+DFS CSU 1563 Lexicography
    stack UVA 442 Matrix Chain Multiplication
  • 原文地址:https://www.cnblogs.com/gr-nick/p/10993823.html
Copyright © 2011-2022 走看看