摄影爱好者玩编程:利用Python和OpenCV打造专业级长时曝光摄影图

开发 开发工具
在本文中,我们将学习如何使用 OpenCV 和图像处理技术来模拟长时曝光图像。

在本文中,我们将学习如何使用 OpenCV 和图像处理技术来模拟长时曝光图像。为了模拟长时曝光,我们采用了对一组图像取平均值的帧平均法。

利用Python和OpenCV打造专业级长时曝光摄影图

长时曝光是摄影师最喜欢的摄影技术之一,运用长时曝光技术可以拍出展示时光流逝的图片,而这是传统技术难以企及的。我们经常使用这种技术表达流光夜景或柔顺的流水。优秀的长时曝光作品是摄影师对快门速度、光圈大小和 ISO 感光度的***把控,那么我们如何使用 Python 和 OpenCV 库来实现这种长时曝光的效果呢?

使用长时曝光技术后,水流变得如丝般光滑,夜空中的星星也随着地球的旋转留下一道光线轨迹,车头/尾灯成为了一束光带。

长时曝光技术效果酷炫,但为了拍到这种类型的镜头,我们需要学习一些系统方法:把相机放在三脚架上,应用各种滤镜,计算曝光值等。更不用说,我们需要成为一名熟练的摄影师!

作为一名计算机视觉研究员和程序员,本文作者知道很多关于图像处理的知识。虽然他是个菜鸟摄影师,但有一种通过应用多帧图像平均法来模拟长时曝光效果的方法。通过计算在特定时间内拍摄的图像的平均值,我们可以(有效)模拟长时间曝光效果。

而且由于视频实际上是一系列的图像,我们可以通过计算视频中的所有帧的平均值来实现长时曝光效果。如此得到的是令人惊叹的长时曝光效果。

一、用 OpenCV 和 Python 实现长时曝光效果

这篇文章分为三部分。在本文的***部分,我们将讨论如何通过帧平均法来模拟长时间曝光效果。随后我们将编写为输入视频创建长时曝光效果的 Python 和 OpenCV 代码。***,我们将在一些样例视频上使用我们的代码,以创建酷炫的长时曝光图像。

1. 通过多帧图像平均法模拟长时曝光效果

通过平均数模拟长时曝光的想法由来已久。事实上,如果我们去浏览热门的摄影网站,就会找到有关如何使用相机和三脚架手动实现这类效果的教程。

我们今天的目标是简单地实现这种方法,所以我们使用 Python 和 OpenCV 自动为输入视频创建长时曝光效果的图像。给定一个输入视频,我们将计算所有帧的平均值(加权平均)以创建长时曝光效果。

注意:我们也可以使用多个连续图像创建这种长时曝光效果,但是由于视频的实质是一系列图像,因此使用视频演示此技术更容易。在将此技术应用到自定义图像时,请牢记这一点。我们看到,代码并不复杂,并且在应用于使用三脚架捕获的视频时(不要抖动相机)效果很好。

2. OpenCV 实现模拟长时曝光效果

我们首先创建一个名为 long_exposure.py 的新文件,然后插入以下代码:

  1. # import the necessary packagesimport argparseimport imutilsimport cv2 
  2.   
  3. # construct the argument parse and parse the argumentsap = argparse.ArgumentParser() 
  4. ap.add_argument("-v", "--video", required=True
  5.     help="path to input video file"
  6. ap.add_argument("-o", "--output", required=True
  7.     help="path to output'long exposure'"
  8. args = vars(ap.parse_args()) 

2-4 行导入软件包,因此我们需要预先安装 Imutils 和 opencv。如果你没有安装 imutils 模块,可以通过 pip 安装:

  1. $ pip install --upgrade imutils 

如果你的电脑没有安装配置 OpenCV,那么请自行搜索 OpenCV 3 的安装教程,并选择适合你系统的安装方式。

我们在 7-12 行解析命令行参数。

  • --video : 视频文件目录路径
  • --output : 输出「长时曝光」图像的路径+文件名

接下来执行一些初始化步骤:

  1. # initialize the Red, Green, and Blue channel averages, along with# the total number of frames read from the file 
  2. (rAvg, gAvg, bAvg) = (None, None, None) 
  3. total = 0 
  4. # open a pointer to the video file 
  5. print("[INFO] opening video file pointer...") 
  6. stream = cv2.VideoCapture(args["video"]) 
  7. print("[INFO] computing frame averages (this will take awhile)...") 

我们在第 16 行初始化 RGB 通道平均值,稍后会将其合并到最终的长时曝光图像中。我们还初始化了第 17 行的总帧数。

对于本教程,我们正在使用包含所有帧的视频文件,因此有必要在 21 行创建一个捕获视频流的文件指针。

现在我们进入计算平均值的循环语句中:

  1. # loop over frames from the video file streamwhile True: 
  2.     # grab the frame from the file stream 
  3.     (grabbed, frame) = stream.read() 
  4.   
  5.     # if the frame was not grabbed, then we have  
  6.     # reached the end of the sfile   
  7.         if not grabbed:      
  8.                  break 
  9.   
  10.     # otherwise, split the frmae into its respective channels 
  11.     (B, G, R) = cv2.split(frame.astype("float")) 

在循环语句中,我们将从流中捕获帧(27 行),并将帧各自分解到对应的 BGR 通道变量(35 行)。请注意循环语句退出条件 :如果未从视频文件流的末尾抓取帧,我们将退出循环(31 行和 32 行)。

我们将在循环语句的其它部分执行平均值计算:

  1. # if the frame averages are None, initialize them 
  2.     if rAvg is None: 
  3.         rAvg = R 
  4.         bAvg = B 
  5.         gAvg = G 
  6.   
  7.     # otherwise, compute the weighted average between the history of 
  8.     # frames and the current frames  
  9.     else: 
  10.         rAvg = ((total * rAvg) + (1 * R)) / (total + 1.0) 
  11.         gAvg = ((total * gAvg) + (1 * G)) / (total + 1.0) 
  12.         bAvg = ((total * bAvg) + (1 * B)) / (total + 1.0) 
  13.   
  14.     # increment the total number of frames read thus far 
  15.     total += 1 

如果这是***次迭代,我们在第 38-41 行上将 RGB 的初始平均值设置为抓取的***帧的通道值(if 语句仅在***次迭代时执行此操作)。

否则,我们将计算 45-48 行上抓取的图像每个通道的平均值。平均值计算非常简单,我们将总帧数乘以通道平均值,加上相应的通道,然后将该结果除以浮点型总帧数(我们将分母总数加一,因为生成的是一个新帧)。我们将计算结果存储在相应的 RGB 通道平均值数组中。

***,我们增加总帧数,以便能够保持运行时平均值(第 51 行)。一旦我们遍历完视频文件中的所有帧,我们就可以将(平均)通道值合并成一个新图像并将其写入磁盘:

  1. # merge the RGB averages together and write the output image to disk 
  2. avg = cv2.merge([bAvg, gAvg, rAvg]).astype("uint8") 
  3. cv2.imwrite(args["output"], avg) 
  4.   
  5. # do a bit of cleanup on the file pointer 
  6. stream.release() 

在 54 行,我们使用 cv2.merge 函数,同时指定了列表中的每个图像的通道平均值。因为这些数组包含浮点数(它们是所有帧的平均值),所以我们需要使用 astype("uint8") 函数将像素值转换为 [0-255] 的整数。

我们使用命令行参数 path + filename 在随后的第 55 行中将 avg 图像写入磁盘。我们也可以通过 cv2.imshow 函数将图像显示在屏幕上,但是由于这会花费大量的 CPU 资源来处理视频文件,所以我们只是将图像保存到磁盘以便进一步查看。

该脚本的***一步是通过释放视频流指针(58 行)来清空内存。

3. 长时曝光效果与 OpenCV 实现对比

我们通过处理三个示例视频来测试脚本效果。请注意,每个视频均由安装在稳定性良好的三脚架上的相机拍摄。

请注意所用视频非作者本人拍摄,但都得到原作者的授权许可; 因此,本文作者并不能提供除源代码之外的视频资源下载。不过,如果你想重现作者的实验结果,请参考我提供的原始视频的链接。

我们的***个示例是 15 秒钟的水冲石头的视频,下面的视频中包含了一个样本帧:

视频地址:https://videohive.net/item/mountain-river-water-and-stones-01/16602591

河水冲击石头的样本帧

图 1 :河水冲击石头的样本帧

我们只需执行以下命令以实现长时曝光效果。

  1. $ time python long_exposure.py --video videos/river_01.mov --output river_01.png  
  2. [INFO] opening video file pointer... 
  3. [INFO] computing frame averages (this will take awhile)... 
  4.   
  5. real    2m1.710s 
  6. user    0m50.959s 
  7. sys 0m40.207s 

通过 Python 和 OpenCV 运用平均帧法实现的 15 秒的河水长时曝光效果图

图2:通过 Python 和 OpenCV 运用平均帧法实现的 15 秒的河水长时曝光效果图。

注意水是如何由平均法处理而得到丝滑的效果。我们继续河流的第二个例子,再次得到一幅蒙太奇效果图如下:

另一条河流的样本帧

图 3: 另一条河流的样本帧

以下命令用于生成长时曝光效果图:

  1. $ time python long_exposure.py --video videos/river_02.mov --output river_02.png  
  2. [INFO] opening video file pointer... 
  3. [INFO] computing frame averages (this will take awhile)... 
  4.   
  5. real    0m57.881s 
  6. user    0m27.207s 
  7. sys 0m21.792s 

第二条河流的丝滑的长时曝光效果图(由 OpenCV 创建)

图 4:第二条河流的丝滑的长时曝光效果图(由 OpenCV 创建)

注意静止的岩石是如何保持原状,但是湍急的河水被平均化为连续的图片,从而模拟出长时曝光效果。

***一个例子是我最喜欢的,因为水的颜色令人赞叹,它使水和森林交相辉映:

激流穿越森林的样本帧

图 5:激流穿越森林的样本帧

当用 OpenCV 产生长时曝光效果时,它会给你一种超现实的梦幻般的感觉:

  1. $ time python long_exposure.py --video videos/river_03.mov --output river_03.png  
  2. [INFO] opening video file pointer... 
  3. [INFO] computing frame averages (this will take awhile)... 
  4.   
  5. real    3m17.212s 
  6. user    1m11.826s 
  7. sys 0m56.707s 

通过使用 Python 和 OpenCV 创建的梦幻般的长时曝光效果图

图 6:通过使用 Python 和 OpenCV 创建的梦幻般的长时曝光效果图

才外,我们还可以考虑通过有规律的间隔从输入,从视频中对帧进行采样而不是对所有帧取平均值来构造不同的输出。

二、总结

在本文中,我们学习了如何使用 OpenCV 和图像处理技术来模拟长时曝光图像。为了模拟长时曝光,我们采用了对一组图像取平均值的帧平均法。我们假设输入图像/视频是使用固定的相机拍摄的(否则产生的输出图像会失真)。虽然这并非真正的「长时曝光」,但是效果上是极其(视觉上)相似的。更重要的是,这允许你应用长时曝光效果,而不需要成为专家摄影师或购买昂贵的相机、镜头和滤镜。

原文:https://www.pyimagesearch.com/2017/08/14/long-exposure-with-opencv-and-python/

【本文是51CTO专栏机构“机器之心”的原创译文,微信公众号“机器之心( id: almosthuman2014)”】

 

戳这里,看该作者更多好文

责任编辑:赵宁宁 来源: 51CTO专栏
相关推荐

2012-02-08 11:39:57

精益扫描仪

2014-02-13 10:15:39

编程业余爱好

2021-10-14 10:45:35

物联网设备技术

2013-12-26 10:32:30

编程学习

2021-01-10 15:31:05

Mac终端Linux

2018-06-12 07:38:05

Linux 开源

2009-11-02 11:25:40

LinuxUbuntu 9.10操作系统

2019-08-09 10:15:14

机器学习人工智能监督学习

2010-06-18 09:00:54

2011-05-30 13:58:56

PHP

2017-03-23 15:17:20

Linuxsudo棋盘

2009-02-27 09:59:07

LinuxFedora 10爱好者

2011-05-11 09:42:27

程序员

2021-01-12 10:50:48

人工智能Instagramer科技

2024-01-29 13:09:39

AI摄影师

2012-12-07 13:15:12

佳能打印机

2011-08-29 17:56:18

Paper Camer拍照应用

2010-05-11 10:08:00

2014-04-01 09:48:09

Linux高清壁纸

2011-01-05 09:36:19

VirtualBSD
点赞
收藏

51CTO技术栈公众号