事件相机的原理和特性在此处不解释,本文只讲解如何配置和使用事件相机模拟器。模拟器是用线性插值和高斯噪声扰动的方式,模拟出事件流来。因此需要帧率尽可能的高一些。亲自测了以后,和DAVIS事件相机实际对着电脑屏幕拍摄的已经记录的视频没什么差别。   模拟器在github网址为:点击这里  

目录

一、模拟器的配置

可能出错以及解决方案:

继续配置:

二、模拟器的使用

(一)跑通demo:

(二)用自己的视频模拟事件流

(三)从bag中解析数据至txt文件中

 

一、模拟器的配置

首先,安装事件相机模拟器,最基本的要求有两点: 1.(必要)必须安装ROS (这个教程网上已经足够多,本文不再介绍) 2.(可能必要,看情况)挂代理  
刚开始的步骤,和github安装上的步骤一样,而且一般不会出问题:  
mkdir -p ~/sim_ws/src && cd ~/sim_ws
catkin init
catkin config --extend /opt/ros/kinetic --cmake-args -DCMAKE_BUILD_TYPE=Release
  之后安装vcstools:  
sudo apt-get install python-vcstool

  之后的三步:  
cd src/
git clone git@github.com:uzh-rpg/rpg_esim.git
vcs-import < rpg_esim/dependencies.yaml
  其中,第二步可能会报错:Permission denied (publickey). fatal: Could not read from remote repository. 解决方法为:git push(clone)失败报错:Permission denied (publickey). fatal: Could not read from remote repository. 在解决这个问题之后,安装pcl-ros:  
sudo apt-get install ros-kinetic-pcl-ros
  安装glfw:  
sudo apt-get install libglfw3 libglfw3-dev
  安装glm:  
sudo apt-get install libglm-dev
  继续安装:  
sudo apt-get install ros-kinetic-hector-trajectory-server
  执行以下操作:  
cd ze_oss
touch imp_3rdparty_cuda_toolkit/CATKIN_IGNORE \
      imp_app_pangolin_example/CATKIN_IGNORE \
      imp_benchmark_aligned_allocator/CATKIN_IGNORE \
      imp_bridge_pangolin/CATKIN_IGNORE \
      imp_cu_core/CATKIN_IGNORE \
      imp_cu_correspondence/CATKIN_IGNORE \
      imp_cu_imgproc/CATKIN_IGNORE \
      imp_ros_rof_denoising/CATKIN_IGNORE \
      imp_tools_cmd/CATKIN_IGNORE \
      ze_data_provider/CATKIN_IGNORE \
      ze_geometry/CATKIN_IGNORE \
      ze_imu/CATKIN_IGNORE \
      ze_trajectory_analysis/CATKIN_IGNORE
  之后,执行关键的一步:  
catkin build esim_ros
  这一步非常重要。最后一定要提示所有的都安装了,0个跳过,0个failed,0个abandon。否则使用功能的时候会出错。   如果直接就成功了,那么恭喜,跳过我下面这部分的分割线继续看。  

可能出错以及解决方案:

  如果有类似如下错误:  
[ 16%] Performing download step (download, verify and extract) for 'luacov'
-- downloading...
 src='https://github.com/keplerproject/luacov/archive/v0.7.tar.gz'
 dst='/home/build/my_build/external_projects/downloads/v0.7.tar.gz'
 timeout='none'
CMake Error at /home/build/my_build/luacov-prefix/src/luacov-stamp/download-luacov.cmake:21 (message):
 error: downloading
'https://github.com/keplerproject/luacov/archive/v0.7.tar.gz' failed
status_code: 1
status_string: "Unsupported protocol"
log: Protocol "https" not supported or disabled in libcurl
Closing connection -1
make[3]: *** [luacov-prefix/src/luacov-stamp/luacov-download] Error 1
make[2]: *** [CMakeFiles/luacov.dir/all] Error 2
make[1]: *** [CMakeFiles/luacov.dir/rule] Error 2
make: *** [luacov] Error 2
  那么可以进行如下操作:先进入cmake的目录下,   然后:  
./bootstrap --system-curl
make
sudo make install
  注意boostrap这个文件夹是在cmake目录下。   (实验室有朋友表示,找不到这个cmake的目录。如果你遇到这种情况,你应该去回忆一下,你到底有没有到官网用源码装过Cmake。因为ROS也自带一个Cmake,如果把ROS卸载掉,就不能使用cmake了,那说明你没有装过。应该去官网安装一个Cmake。我随便找个教程:Ubuntu 下cmake的安装(以ubuntu16.04,cmake-3.14.0为例,其他版本也可进行参考))   执行完上面的操作后,在刚刚的sim_ws文件夹下重新:  
catkin build esim_ros
  检查是否是否所有的都被安装了。如果仍然有错误(一般就是那个assimp有问题),那么参见: ubuntu下配置ss教程   要按我教程中的去做。(这个链接已经打不开了,相关内容不让发,总之是想挂上out网,所以各位自己想办法吧,国内的网配这个可能有点问题)   做完以后,之后重新执行该指令:(proxychains是一个指令,在终端里访问那什么的。把网的事情搞定了,这个指令不用也行,直接catkin build esim_ros即可)  
proxychains catkin build esim_ros
  可以看到所有的都被安装了,0个fail,0个abondan。  

继续配置:

  在执行完catkin build指令后,  
echo "source ~/sim_ws/devel/setup.bash" >> ~/setupeventsim.sh
chmod +x ~/setupeventsim.sh
  然后打开bashrc文件:  
sudo gedit ~/.bashrc
  在bashrc文件最末尾,加上这句话:  
alias ssim='source ~/setupeventsim.sh'
 

二、模拟器的使用

  这里只讲解,如何根据video,模拟生成对应的模拟事件流rosbag。(即:Simulating events from a video) 模拟器其余功能的介绍见 事件相机(Event-based camera)模拟器功能介绍  

(一)跑通demo:

 
mkdir -p /tmp/cheetah_example
cd /tmp/cheetah_example
  这里保存在根目录的tmp文件下,重启后会自动删除。之所以样例中会这样,可能是因为这样路径长度比较短。 然后,下载安装这个:  
pip install youtube-dl

  这个不挂代理也能成功的装上。  
youtube-dl https://youtu.be/THA_5cqAfCQ -o cheetah

  上面这步相当于是下载视频,然后保存名字为cheeetah。要想这步执行成功,必须按照上面配置的ss方法中,把终端也挂上代理。实测发现proxychains这个指令在这不顶用了,还是应该开启代理,然后在当前终端里输入  
export http_proxy=socks5://127.0.0.1:1080
  (这个是按照你自己的代理设置配的)   然后重新执行上面的下载视频指令。   切割视频:  
ffmpeg -i cheetah.mkv -ss 00:02:07 -t 00:00:40 -async 1 -strict -2 cheetah_cut.mkv
  resize视频:  
ffmpeg -i cheetah_cut.mkv -vf scale=640:-1 -crf 0 cheetah_sd.mkv
  然后预处理:  
mkdir frames
ffmpeg -i cheetah_sd.mkv frames/frames_%010d.png
  之后决定启动模拟器(一定要打开,在当前终端有效,否则之后会报错):  
ssim

重新开启一个终端,把roscore也打开:  
roscore
  然后回到之前的终端,执行:  
roscd esim_ros
python scripts/generate_stamps_file.py -i /tmp/cheetah_example/frames -r 1200.0
  然后执行:  
rosrun esim_ros esim_node \
 --data_source=2 \
 --path_to_output_bag=/tmp/out.bag \
 --path_to_data_folder=/tmp/cheetah_example/frames \
 --ros_publisher_frame_rate=60 \
 --exposure_time_ms=10.0 \
 --use_log_image=1 \
 --log_eps=0.1 \
 --contrast_threshold_pos=0.15 \
 --contrast_threshold_neg=0.15
  (如果这一步提示,没有找到esim_node,首先要判断,在当前终端里有没有执行ssim这个指令启动模拟器,有没有在外面打开roscore。如果还是找不到,说明最之前的catkin build就没有成功,回到上面重新弄吧!)   重新开启终端:  
rosrun dvs_renderer dvs_renderer events:=/cam0/events

  重新开启终端:  
rosbag play /tmp/out.bag -l -r 0.1.

  然后重新开启终端:  
rqt_image_view /dvs_rendering
  可以看到一个豹子在上面跑。这样样例的demo就跑通了。  

(二)用自己的视频模拟事件流

  这个如果需要切割,或者resize,就可以模仿样例中的ffmpeg使用方式来。这个不属于模拟器的范畴。   反正,首先,把你要用的视频,(未必是样例中的mkv格式,mp4什么的都可以),拷贝到home下面的文件夹下。(因为样例中的tmp文件夹重启后会清空。   例如,拷贝到/home/zhaokai/sim_ws/MOT/下:   这样sim_ws下面有一个名为MOT的文件夹,里面放着一个视频,例如:MOT16-04.mp4   然后:  
cd /home/zhaokai/sim_ws/
mkdir frames
ffmpeg -i MOT16-04.mp4 frames/frames_%010d.png
  然后发现ffmpeg把视频拆成了帧,放到了frames文件夹下。  
ssim
roscd esim_ros
python scripts/generate_stamps_file.py -i /home/zhaokai/sim_ws/MOT/frames -r 1200.0
  注意: 1.这里路径是不认“~”这种符号的。如果写成了 ~/sim_ws会报错的,要从根目录下面开始写。   2.1200表示的是输入视频的帧率。如果你不知道输入视频的帧率,那就可以打开那个视频,看看时长。然后看看frames文件夹下拆成了多少帧,二者一除就知道了。   之后,执行模拟器指令(要确保另一个终端里开着roscore)  
rosrun esim_ros esim_node \
 --data_source=2 \
 --path_to_output_bag=/home/zhaokai/sim_ws/MOT/out.bag \
 --path_to_data_folder=/home/zhaokai/sim_ws/MOT/frames \
 --ros_publisher_frame_rate=60 \
 --exposure_time_ms=10.0 \
 --use_log_image=1 \
 --log_eps=0.1 \
 --contrast_threshold_pos=0.15 \
 --contrast_threshold_neg=0.15
  指令的介绍见github上的解释。 这样执行完,就生成了一个out.bag文件。 然后还是和之前跑样例一样,开一个新的终端:  
rosrun dvs_renderer dvs_renderer events:=/cam0/events
  这里 -l 表示是循环播放。-r表示是慢速播放。如果你想跟原视频同步,那么把 指令中的-r 0.1给去掉。   开一个新的终端:  
rqt_image_view /dvs_rendering
  可以看到你的视频模拟成了事件流。   最后的输出保存在bag文件中。如果想把其中的事件,读成txt文本的形式,读出其中模拟的的x,y,p,t,需要解析bag包。

(三)从bag中解析数据至txt文件中

  记录为bag后,不太熟悉ros的或者只专注于图像的同学可能对于如何把这个bag解析为txt文件有些疑惑。我刚刚接触的时候也比较疑惑,一个月前我对ros就懂一些皮毛。在这个上面走了一些弯路,特意把过程记录在这里,一个目的是能帮助到大家,第二个目的是方便我回头再看。(因为不写下来的话很快就会忘记)   先分析一下,播放这个bag文件:  
ssim
rosbag play out.bag -l -r 0.1
  然后再开启一个终端:  
rostopic list
  可以看到,输出为:  
/cam0/camera_info /cam0/events /cam0/image_corrupted /cam0/image_raw /clock /rosout /rosout_agg
  很明显,播放的话题就是这个/cam0/events   我们需要看一下这个话题的格式:  
rostopic type /cam0/events
  可以看到输出为:  
dvs_msgs/EventArray
  或者用info语句来看:  
rostopic info /cam0/events
  可以看到输出为:  
Type: dvs_msgs/EventArray Publishers: * /play_1579125764739259831 (http://zhaokai-System:33439/) Subscribers: None
  这时,两种方式都显示话题/cam0/events的发布类型为:dvs_msgs/EventArray   因此,再用rosmsg查看一下这个类型的具体内容:  
rosmsg show dvs_msgs/EventArray
  或者也可以直接用roscd指令,到具体文件夹下拿vi指令去查看这个类型的定义:  
roscd dvs_msgs
ls
cd msg
vi EventArray.msg
  可以看到输出为:  
std_msgs/Header header uint32 seq time stamp string frame_id uint32 height uint32 width dvs_msgs/Event[] events uint16 x uint16 y time ts bool polarity
  现在我们要做的就是把里面的events给保存到txt当中。  

弯路:

  刚开始我打算写一个监听程序,把顺便把内容给记录下来。由于CPP版本的程序还需要再编译,所以随手写了一个脚本,想在callback里面把它记录在txt当中。先随便试了一下,随便打印点什么东西:  
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
from dvs_msgs.msg import EventArray
 
EVENT=[]
 
def callback(data):
    #events=data.events
    #global EVENT
    #EVENT.append(events)
    global flag
    a=data.header.stamp
    if(flag):
        print("first msg")
        first=a
        flag=False
    else:
        print(a)
    
 
def listener():
 
    # In ROS, nodes are uniquely named. If two nodes with the same
    # node are launched, the previous one is kicked off. The
    # anonymous=True flag means that rospy will choose a unique
    # name for our 'listener' node so that multiple listeners can
    # run simultaneously.
    rospy.init_node('listener', anonymous=True)
 
    rospy.Subscriber("/cam0/events", EventArray, callback)
 
    # spin() simply keeps python from exiting until this node is stopped
    rospy.spin()
 
if __name__ == '__main__':
    flag=True
    listener()
  结果发现打印频率和监听频率对不上,每次输出的都不太一样(有的给漏掉了),说明不能用这这种方法。后来听别人的,又在rospy.Subscriber("/cam0/events", EventArray, callback)中加了一堆消息队列和缓冲序列,还是有问题。   通过这种方式在终端中运行python脚本,首先是需在终端中启用ssim,其次,如果安装了anaconda的话,需要在~/.bashrc下面把anaconda的环境变量注释掉。python3是不能用rospy的,会报错,找不到rospkg。因此在命令行里的python应该是python2才行,所以需要去掉环境变量中的anaconda去掉,让默认启动的python是2而不是3。   之后又想通过matlab解析这个bag包,结果用github上的开源版本,bug一堆,关键这个消息还是自己定义的,可谓浪费时间。(还花了段时间在ubuntu下面装matlab,感觉自己简直像弱智)   最后,发现python本来就有方法读取,rosbag就可以了。至于里面的数据结构怎么定义的,可以拿关键字“dir”写在脚本里看一下。试了好一会,最后写完的代码如下:  
#!/usr/bin/env python
import rosbag
from tqdm import tqdm
bag=rosbag.Bag('out.bag')
with open("out.txt",'w') as f: 
    for i,(topic,msgs,t) in enumerate(bag.read_messages(topics=['/cam0/events'])):
        for single_event in tqdm(msgs.events):  
            f.write(str(single_event.ts.secs+single_event.ts.nsecs*1.0/1000000000)+' ')         
            f.write(str(single_event.x)+' ')
	    f.write(str(single_event.y)+' ')
            if(single_event.polarity==True):        
	        f.write("1")
            else:
                f.write("0")
	    f.write('\n')  
        print("Writing events between two pic, now is "+ str(i)+'!')
print("Writing is over!")
bag.close()
  把这个文件命名为reader.py,放置在生成的out.bag同一路径下。   注意,为了可视化进度,我安装了tqdm。安装指令:  
pip install tqdm
  之后通过如下指令,解析bag,记录到txt中:  
ssim
python reader.py