事件相机的原理和特性在此处不解释,本文只讲解如何配置和使用事件相机模拟器。模拟器是用线性插值和高斯噪声扰动的方式,模拟出事件流来。因此需要帧率尽可能的高一些。亲自测了以后,和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