OpenCV绘制检测结果

opencv  rtcp  timestamp 

一、介绍

由于在验证阶段,使用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

在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关掉就可以解决这个问题了。

OpenCV绘制检测结果的更多相关文章

  1. OpenCV矩形检测

    OpenCV矩形检测 需求:提取图像中的矩形,图像存在污染现象,即矩形区域不是完全规则的矩形. 思路一:轮廓法 OpenCV里提取目标轮廓的函数是findContours,它的输入图像是一幅二值图像, ...

  2. opencv直线检测在c#、Android和ios下的实现方法

    opencv直线检测在c#.Android和ios下的实现方法 本文为作者原创,未经允许,不得转载 :原文由作者发表在博客园:http://www.cnblogs.com/panxiaochun/p/ ...

  3. OPENCV条形码检测与识别

    条形码是当前超市和部分工厂使用比较普遍的物品,产品标识技术,使用摄像头检测一张图片的条形码包含有两个步骤,第一是定位条形码的位置,定位之后剪切出条形码,并且识别出条形码对应的字符串,然后就可以调用网络 ...

  4. keras系列︱人脸表情分类与识别:opencv人脸检测+Keras情绪分类(四)

    引自:http://blog.csdn.net/sinat_26917383/article/details/72885715 人脸识别热门,表情识别更加.但是表情识别很难,因为人脸的微表情很多,本节 ...

  5. 详解用OpenCV绘制各类几何图形

    摘要:本文详细介绍了OpenCV绘制几何图形的方法,利用cv2.line().v2.circle().cv2.rectangle().cv2.ellipse().cv2.polylines().cv2 ...

  6. OpenCV——人脸检测

    OpenCV支持的目标检测的方法: 利用样本的Haar特征进行的分类器训练,得到的级联boosted分类器(Cascade Classification) 1.加载级联分类器 CascadeClass ...

  7. OpenCV轮廓检测,计算物体旋转角度

    效果还是有点问题的,希望大家共同探讨一下 // FindRotation-angle.cpp : 定义控制台应用程序的入口点. // // findContours.cpp : 定义控制台应用程序的入 ...

  8. OpenCV人形检测Hog

    #include "iostream" #include "queue" using namespace std; #include "opencv2 ...

  9. opencv人脸检测分类器训练小结

    这两天在初学目标检测的算法及步骤,其中人脸检测作为最经典的算法,于是进行了重点研究.该算法最重要的是建立人脸检测分类器,因此我用了一天的时间来学习分类器的训练.这方面的资料很多,但是能按照一个资料运行 ...

随机推荐

  1. CodeForces 367E Sereja and Intervals

    CodeForces 3 67E (109 + 7). Two ways are considered distinct if there is such j(1 ≤ j ≤ n), that the ...

  2. 常州模拟赛d8t2 绘画

    分析:考虑记录每个坐标上每个颜色出现了几次,并由此算出每个颜色在这个坐标上的贡献.答案肯定是原图的答案扣去矩形的答案,再加上那个矩形同种颜色的贡献,这里的答案指的是Σdis.我们先要记录每个颜色在各个 ...

  3. codevs1001 舒适的线路

    题目描述 Description Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光.Z小镇附近共有N(1<N≤500)个景点(编号为1,2,3,…,N),这些景点被M(0<M≤ ...

  4. Mysql 使用delete drop truncate 删除数据时受外键约束影响解决方案

    先禁用数据库的外键约束: set foreign_key_checks=0; 进行删除操作 delete.drop.truncate 恢复数据库外键约束: set foreign_key_checks ...

  5. docker重新打包MySQL5.7镜像

    1:先下载MySQL镜像 # docker pull  mysql:5.7   2:运行镜像生成容器 # docker run --name mysql -p 3306:3306 -e MYSQL\_ ...

  6. - > 贪心基础入门讲解四——独木舟问题

    n个人,已知每个人体重,独木舟承重固定,每只独木舟最多坐两个人,可以坐一个人或者两个人.显然要求总重量不超过独木舟承重,假设每个人体重也不超过独木舟承重,问最少需要几只独木舟? 分析: 一个显然的策略 ...

  7. AE 创建

    using System; using System.Drawing; using System.Runtime.InteropServices; using ESRI.ArcGIS.ADF; usi ...

  8. Solidworks在哪里找到内六角螺钉 内六角螺栓保准件

    GB-screws-凹头螺钉-出来了  

  9. UIWebView 设置背景为透明

    UIWebView的背景怎样设置成为透明? [webview setBackgroundColor:[UIColor clearColor]]; [webview setOpaque:NO]; 两句代 ...

  10. JAVA高速开发平台 - 开源 免费 - JEECG

    JEECG 微云高速开发平台 当前最新版本号: 3.6.2(公布日期:20160315) 下载地址:http://git.oschina.net/jeecg/jeecg 前言: 随着 WEB UI 框 ...