前言

  本文是对MIT多媒体实验室论文《Eulerian video magnification for revealing subtle changes in the world》对应代码的研读。Hao-Yu Wu的这篇EVM研究算得上是EVM的开山鼻祖,在视频放大领域久负盛名,网上关于这篇论文的研读广有流传。但对于该论文所对应的MATLAB代码却鲜有注解,对于初学者而言,读懂EVM代码并非易事。
  本文对EVM相关代码进行解读,文中并不涉及具体理论知识的讲解,仅从代码复现的角度进行注解。

函数流程

  amplify_spatial_lpyr_temporal_iir函数提供了一种放大算法,即空间滤波采用拉普拉斯金字塔,时域滤波采用IIR滤波器。该函数的主要算法流程如下图所示,其中,拉普拉斯金字塔是全文的核心所在。而拉普拉斯金字塔在EVM代码中又分为构造(buildLpyr函数)和重构(reconLpyr函数)两个部分。

源码解读

  下面我们逐行对amplify_spatial_lpyr_temporal_iir函数解读——

%% vidFile 视频源  resultsDir 生成视频的路径  alpha α参数  lambda_C λ_c r1 r2 参数r1 参数 r2
function amplify_spatial_lpyr_temporal_iir(vidFile, resultsDir, ...alpha, lambda_c, r1, r2, chromAttenuation)% vidName 从输入视频路径中分离视频名,方便下一步创建新的输出视频名[~,vidName] = fileparts(vidFile);% outName 视频输出指针,并且对输出视频命名以及定义格式outName = fullfile(resultsDir,[vidName '-iir-r1-' num2str(r1)...'-r2-' num2str(r2)...'-alpha-' num2str(alpha) ...'-lambda_c-' num2str(lambda_c) ...'-chromAtn-' num2str(chromAttenuation) '.avi']);% 对视频进行处理,将文件指针赋给变量 vidvid = VideoReader(vidFile);% 提取视频的具体信息% vidHeight 视频高度 vidWidth 视频宽度 nChannels 通道数  fr 视频帧率 len 视频总帧数vidHeight = vid.Height;vidWidth = vid.Width;nChannels = 3;fr = vid.FrameRate;len = vid.NumberOfFrames;% temp是一个结构体,成员包括 cdata,一个全零矩阵,颜色映射,空矩阵,作用是容纳视频帧temp = struct('cdata', ...zeros(vidHeight, vidWidth, nChannels, 'uint8'), ...'colormap', []);% 参数赋初值  startIndex 初始帧  endIndex 总帧数-10,最后几帧的视频质量可能有问题,故舍去startIndex = 1;endIndex = len-10;% 读视频,把输出视频指针赋给vidOut  把视频源的帧率赋给输出视频vidOut = VideoWriter(outName);vidOut.FrameRate = fr;

  以上代码的主要目的是初始化相关控制变量,为下一步的读视频和滤波做准备。下面开始执行读视频和获取视频第一帧信息的相关操作——

    %打开输出视频指针open(vidOut)% firstFrame  第一帧的处理% 将视频的第一帧读取并传给temp的cdata成员% 这样做的目的是获取处理后的数据的各种信息,相当于是【预处理】temp.cdata = read(vid, startIndex);% 返回与视频帧相关联的图像数据[rgbframe,~] = frame2im(temp);% 将图像传值给双精度rgbframe = im2double(rgbframe);% 将RGB颜色值传给NTSC颜色域frame = rgb2ntsc(rgbframe);% 调用buildLpyr函数,获取pyr的值% 这个【pyr很重要,下一步作为iir滤波器的参数】[pyr,pind] = buildLpyr(frame(:,:,1),'auto');pyr = repmat(pyr,[1 3]);[pyr(:,2),~] = buildLpyr(frame(:,:,2),'auto');[pyr(:,3),~] = buildLpyr(frame(:,:,3),'auto');lowpass1 = pyr;lowpass2 = pyr;reLpyr = rgbframe;writeVideo(vidOut,im2uint8(reLpyr));% nLevels也很重要,功能暂时不明确nLevels = size(pind,1);

  以上代码的主要作用是获取视频第一帧并做滤波,其目的是得到视频帧的相关信息,以及通过对第一帧构建拉普拉斯金字塔来初始化IIR滤波器的矩阵大小。
  在这些信息获取之后,后面的视频序列就可以以同样的方式进行处理,下面的for循环是整个程序的核心所在,从第二帧开始进行拉普拉斯金字塔+IIR滤波器的变换,同时根据论文对放大倍数的限制进行判断,进行适当的放大,得到最终想要的结果。

    % i从 第一帧+1 到最后一帧的for循环for i=startIndex+1:endIndex% 调用progmeter函数,表示处理视频的进度progmeter(i-startIndex,endIndex - startIndex + 1);% vid是视频源指针,i代表当前帧,temp是对帧以及相关信息的复合结构体% 读取vid中第i帧图像,然后赋值给temp的cdata成员temp.cdata = read(vid, i);[rgbframe,~] = frame2im(temp);rgbframe = im2double(rgbframe);frame = rgb2ntsc(rgbframe);% 拉普拉斯金字塔分别对每个颜色通道进行空间滤波% 得到的pyr是一维矢量,便于计算[pyr(:,1),~] = buildLpyr(frame(:,:,1),'auto');[pyr(:,2),~] = buildLpyr(frame(:,:,2),'auto');[pyr(:,3),~] = buildLpyr(frame(:,:,3),'auto');% 时域处理 IIR滤波器% 这里的IIR需要借助拉普拉斯金字塔计算后的信息lowpass1 = (1-r1)*lowpass1 + r1*pyr;lowpass2 = (1-r2)*lowpass2 + r2*pyr;filtered = (lowpass1 - lowpass2);% 对应论文中图6对每一个空间频带进行放大(以下部分是信号放大部分)ind = size(pyr,1);% 对应论文图6中 (1+α)δ(t)<λ/8delta = lambda_c/8/(1+alpha);% 论文中提到的提高α的参数(为了更好的显像效果) exaggeration_factor = 2;% 计算拉普拉斯金字塔中最低的空间频带具有代表性的波长λ% lambda的计算方式是经验规律lambda = (vidHeight^2 + vidWidth^2).^0.5/3; % 3 is experimental constant 3是实验常量%% for l = nLevels:-1:1indices = ind-prod(pind(l,:))+1:ind;% compute modified alpha for this level% 对于这一层,计算修改过的αcurrAlpha = lambda/delta/8 - 1;currAlpha = currAlpha*exaggeration_factor;if (l == nLevels || l == 1) % 忽略最高和最低频带filtered(indices,:) = 0;elseif (currAlpha > alpha)  % 超出λ_c的λ值filtered(indices,:) = alpha*filtered(indices,:);elsefiltered(indices,:) = currAlpha*filtered(indices,:);endind = ind - prod(pind(l,:));lambda = lambda/2; end

  以上代码完成了这个程序的主要功能。不难发现,buildLpyr函数和reconLpyr函数承担了大量功能,关于这两个函数,这里不再详细展开,只知道它们是用来构建拉普拉斯金字塔以及重构帧即可。
  最后是将放大结果加回到原视频帧中,使得放大效果可视化。

            %% 对输入视频操作reLpyr = zeros(size(frame));% 重构金字塔reLpyr(:,:,1) = reconLpyr(filtered(:,1),pind);reLpyr(:,:,2) = reconLpyr(filtered(:,2),pind);reLpyr(:,:,3) = reconLpyr(filtered(:,3),pind);% 然后放大amplification = zeros(size(reLpyr));amplification(:,:,1) = reLpyr(:,:,1);amplification(:,:,2) = reLpyr(:,:,2)*chromAttenuation; amplification(:,:,3) = reLpyr(:,:,3)*chromAttenuation;% 然后加回原视频帧output = frame + amplification;     output = ntsc2rgb(output); % filtered = rgbframe + filtered.*mask;% 归一化处理output(output > 1) = 1;output(output < 0) = 0;% 视频写入writeVideo(vidOut,im2uint8(output));end % 整个处理流程的for循环结束了close(vidOut);
end

总结

  amplify_spatial_lpyr_temporal_iir函数本身并不复杂,但其背后调用了大量matlablabPyrTools开源库中的函数,相对而言,该开源库更加生涩难懂。matlablabPyrTools是MIT研发出来的一个开源库,提供了大量数字图像处理方面的经典算法以及一些MIT研究成果。

EVM源码解读(1):amplify_spatial_lpyr_temporal_iir函数相关推荐

  1. 网页游戏开发例子php,PHP网页游戏学习之Xnova(ogame)源码解读(八)

    这篇文章主要介绍了PHP网页游戏Xnova(ogame)源码解读的公共函数部分,需要的朋友可以参考下 十一.公共函数(functions.php) 本来打算写建筑页面的分析,但是建筑页面东西比较多,一 ...

  2. golang源码解读之 net.Dial

    1.调用 例子 conn, err := net.Dial("tcp", "google.com:80") if err != nil {// handle e ...

  3. PostgreSQL 源码解读(147)- Storage Manager#3(fsm_search函数)

    本节简单介绍了PostgreSQL在执行插入过程中与存储相关的函数RecordAndGetPageWithFreeSpace->fsm_search,该函数搜索FSM,找到有足够空闲空间(min ...

  4. Bert系列(二)——源码解读之模型主体

    本篇文章主要是解读模型主体代码modeling.py.在阅读这篇文章之前希望读者们对bert的相关理论有一定的了解,尤其是transformer的结构原理,网上的资料很多,本文内容对原理部分就不做过多 ...

  5. Bert系列(三)——源码解读之Pre-train

    https://www.jianshu.com/p/22e462f01d8c pre-train是迁移学习的基础,虽然Google已经发布了各种预训练好的模型,而且因为资源消耗巨大,自己再预训练也不现 ...

  6. linux下free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  7. nodeJS之eventproxy源码解读

    1.源码缩影 !(function (name, definition) { var hasDefine = typeof define === 'function', //检查上下文环境是否为AMD ...

  8. PyTorch 源码解读之即时编译篇

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 作者丨OpenMMLab 来源丨https://zhuanlan.zhihu.com/ ...

  9. Alamofire源码解读系列(九)之响应封装(Response)

    本篇主要带来Alamofire中Response的解读 前言 在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下.我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然, ...

最新文章

  1. seaborn使用Catplot函数可视化水平小提琴图(Make Horizontal Violin Plot with Catplot in Seaborn)
  2. 数据挖掘具体技术——分类
  3. Android Studio打开DDMS : An error has occurred URIUtil
  4. SAE去掉index.php实现自定义固定链接
  5. java 任意数平均值_【编程题】通过键盘输入三个任意的数字,计算三个值的平均值,并输出结果。...
  6. 深入了解ASP.NET运行内幕
  7. 在Linux上安装Mysql 以及 涉及问题
  8. ztree同级只显示一个节点
  9. pytorch实现attention_Self-Attention手动推导及实现
  10. 运营到底是做什么的?
  11. win7删除桌面计算机图标怎么删除,Windows7电脑桌面ie图标怎么删除不了?
  12. DSP28m35的IPC通讯编程经验
  13. 数据可视化——tableau 数据报表样例(报表模板)
  14. 【桧木】桧木精油的功效 台湾桧木价值所在
  15. Java 时间差运算工具函数(时间戳运算)
  16. caj转word怎么转,怎么将caj转换成word
  17. Java获取汉字的拼音
  18. HyperLynx(十六)PCI-E的设计与仿真
  19. C语言中汉字的存储和输出
  20. DrawBoard 是一个自定义 View 实现的画板;方便对图片进行各种编辑或涂鸦相关操作

热门文章

  1. 舒城中学2021年高考成绩查询,舒城教育信息网——舒城县教育局关于对2019年荣获高考目标奖的学校和个人表彰的通报...
  2. [原创]数字转换中文大写金额
  3. 纷享销客标讯通,大客招标经营的杀手锏
  4. Unity3d中UGUI组件精简复盘(十九)ContentSizeFitter组件
  5. 五大浏览器js 判断IE、Firefox、Safari、Chrome、Opera
  6. MFC edit control动态设置密码
  7. JZ38* 字符串的排列
  8. 由Sensor光谱响应曲线联想到的白平衡增益计算
  9. 适合程序员的 5 款 Linux 发行版
  10. StbM 和 Time Synchronization Over CAN and Ethernet(二) 以EthTSyn和StbM为例