实战:基于OpenCV进行长时间曝光(内含彩蛋)

小白学视觉

共 6751字,需浏览 14分钟

 ·

2021-03-25 10:13

点击上方小白学视觉”,选择加"星标"或“置顶

重磅干货,第一时间送达


在本文中,我们将学习长时间曝光摄影技术,以及如何使用Python和OpenCV(开源计算机视觉库)对其进行仿真。

一、什么是“长时间曝光“?


直接来自维基百科:

长时间曝光,时间曝光或慢速快门摄影涉及使用长时间快门速度来清晰地捕获图像的静止元素,同时使运动元素模糊。长时间曝光摄影可以捕捉到传统摄影无法捕捉到的一个元素:较长的时间。


因此,长时间曝光是一种出色的摄影技术,包括创建可显示时间效果的图像,这是普通摄影无法捕获的。对于初学者来说,这种技术并不容易,因为它需要一种有条理的方法来捕获图像。幸运的是,我们可以用图像处理来模拟这种技术。为此,我们可以使用一个视频(基本上是一系列图像)来计算基于所有视频帧的平均图像。


二、依赖库


在本教程中,我们将使用Python 3和OpenCV。我们将使用OpenCV,因为它是一个著名的开源计算机视觉库,其中提供了许多处理图像和视频的功能。在本教程中,我们将使用OpenCV来操纵视频及其帧。下图优雅地说明了为什么要使用Python:

此外,我们将使用其他两个Python软件包:

  • 单击:提供命令行界面(CLI)。
  • tqdm:在终端上显示一个优雅的进度栏。

我们还将使用Pipenv轻松创建和管理虚拟环境。


三、环境


使用项目根目录中提供的Pipfile,我们可以使用Pipenv通过以下命令创建,安装依赖项并激活虚拟环境:

pip install pipenv # if not installed yetpipenv install --devpipenv shell
四、代码

让我们开始看“入口点”。这是一个CLI命令,我们应该在其中传递一些参数:
  • video_path:本地计算机中视频的路径。
  • image_path:输出图像的路径和文件名。
  • step / -s(可选):用于获取帧的步骤。这是一个可选参数,默认值为1。
@cli.command()@click.argument("video_path", nargs=1, type=str)@click.argument("image_path", nargs=1, type=str)@click.option(    "--step",    "-s",    default=1,    type=int,    show_default=True,    help="Step used to get the frames.",)def local_video(video_path, image_path, step):    """Apply the long exposure algorithm to a local video."""    long_exposure = LongExposure(video_path, image_path, step)    long_exposure()

它使用单击修饰符来提供漂亮的命令行界面。如果你们仍然不知道单击,我们建议你们看一下该程序包。这使我们可以像这样调用Python脚本:

python src/long_exposure.py local-video video.mp4 image.png -s 5
如果你们对如何使用有疑问,可以通过help标志来调用它:
python src/long_exposure.py --help

现在,让我们谈谈LongExposure类。这是项目的主类,它在其__init__中接收以下参数:

  • video:本地计算机中的视频路径。
  • output_image_path:必须在其中保存输出图像的路径/文件名。
  • step:用于忽略某些帧的步骤(默认为1)。这对于长视频非常有用。
class LongExposure:    def __init__(self, video, output_image_path, step=1):        self.video = video        self.output_image_path = output_image_path        self.step = step

平均器的方法仅仅是一个Clojure的是用来递增地计算平均图像,因为我们正在消耗视频帧逐个。

 @staticmethod    def averager():        """Calculate the average using a clojure."""        count = 0        total = 0.0
def average(value): nonlocal count, total count += 1 total += value return total / count
return average

由于这是一个可调用的类,因此__call__方法是该类的主要方法,它主要负责:

  • 加载视频;

  • 通过视频帧进行迭代以计算平均图像(针对每个颜色通道);

  • 合并颜色通道;以及

  • 将映像保存在磁盘上。

def __call__(self):        logging.info("Processing video %r with step %r", self.video, self.step)
# Open a pointer to the video file stream = cv2.VideoCapture(self.video)
# Get the total frames to be used by the progress bar total_frames = int(stream.get(cv2.CAP_PROP_FRAME_COUNT))
r, g, b = None, None, None r_avg, g_avg, b_avg = self.averager(), self.averager(), self.averager()
for count in tqdm(range(total_frames)): # Split the frame into its respective channels _, frame = stream.read()
if count % self.step == 0: # Get the current RGB b_curr, g_curr, r_curr = cv2.split(frame.astype("float")) r, g, b = r_avg(r_curr), g_avg(g_curr), b_avg(b_curr)
# Merge the RGB averages together and write the output image to disk avg = cv2.merge([b, g, r]).astype("uint8") logging.info("Saving image as %r", self.output_image_path) cv2.imwrite(self.output_image_path, avg)
# Release the stream pointer stream.release()

请注意,cv2来自OpenCV,而tqdm(在循环中)仅用于显示进度条。如你们所见,这里没有魔术,代码非常简单,几乎可以自己解释。现在,让我们看看结果。


五、结果


我已使用以下YouTube视频应用长时间曝光效果:

推荐一波我好朋友的公众号:


我隐藏的这么深还是被幸运的你发现了,为了感谢各位小伙伴一直以来对小白的支持,我们决定发个百元红包(¥10*10),点击文字就可以来抽取!另外,不要忘了签到换书哦!


下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。

下载2:Python视觉实战项目52讲
小白学视觉公众号后台回复:Python视觉实战项目即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。

下载3:OpenCV实战项目20讲
小白学视觉公众号后台回复:OpenCV实战项目20讲即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。

交流群


欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~


浏览 30
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报