代码及其注释
function amplify_spatial_lpyr_temporal_iir(vidFile, resultsDir, ...
alpha, lambda_c, r1, r2, chromAttenuation)
% ~ 在这里表示忽略输出参数,在这里为什么不涉及扩展名?
[~,vidName] = fileparts(vidFile);%从结果上我们可以看到只提取了名字,并没有提取到后缀
outName = fullfile(resultsDir,[vidName '-iir-r1-' num2str(r1)...
'-r2-' num2str(r2)...
'-alpha-' num2str(alpha) ...
'-lambda_c-' num2str(lambda_c) ...
'-chromAtn-' num2str(chromAttenuation) '.avi']);
% Read video
vid = VideoReader(vidFile);%创建对象vid,其中括号里的是该对象读取的视频路径
% Extract video info 获取视频信息
vidHeight = vid.Height;
vidWidth = vid.Width;
nChannels = 3;
fr = vid.FrameRate;
len = vid.NumberOfFrames;
temp = struct('cdata', ...
zeros(vidHeight, vidWidth, nChannels, 'uint8'), ...
'colormap', []);%这里的数据存在空矩阵里还是零矩阵里
startIndex = 1;
endIndex = len-10;%为什么去掉结尾的十帧
vidOut = VideoWriter(outName);%创建一个视频对象vidOut同时对其进行命名为outName,可以将若干图像转换为视频
vidOut.FrameRate = fr;
open(vidOut)
% firstFrame
temp.cdata = read(vid, startIndex);%获取该视频对象的第一帧的所有数据
[rgbframe,~] = frame2im(temp);%将得到的第一帧的帧数据转化为图像数据 frame2im将电影帧转换为索引图像。
%[x,map]=frame2im(f)将单个电影帧f转换为索引图像x和关联的颜色映射。电影帧是getframe或im2frame的结果。如果帧包含TrueColor数据,则映射为空。
rgbframe = im2double(rgbframe);%常用的是im2double函数,将uint8图像转为double类型,范围为0-1,如果是255的图像,那么255转为1,0还是0,中间的做相应改变。
frame = rgb2ntsc(rgbframe);%yiqmap=rgb2ntsc(rgbmap)将rgbmap中的m-by-3 rgb值转换为ntsc颜色空间。Yiqmap是一个m-by-3矩阵,其中包含NTSC亮度(Y)和色度(I和Q)颜色分量,这些分量作为等同于RGB颜色映射中颜色的列。
%yiq=rgb2ntsc(rgb)将TrueColor图像rgb转换为等效的ntsc图像yiq。
[pyr,pind] = buildLpyr(frame(:,:,1),'auto');%pyr列向量696325*1,第1个元素是拉普拉斯金字塔的第8层(最底层)960*540的[1 1]元素,第696325个元素是第一层(最高层)8*5的[8 5]元素
%pind各层大小
%拉普拉斯金字塔的第2到第8层都是高斯金字塔同层减去上采样图像,但是拉普拉斯金字塔的第一层就是高斯金字塔,第一层不进行下采样,不进行相减操作。
pyr = repmat(pyr,[1 3]);
[pyr(:,2),~] = buildLpyr(frame(:,:,2),'auto');
[pyr(:,3),~] = buildLpyr(frame(:,:,3),'auto');
lowpass1 = pyr;
lowpass2 = pyr;
output = rgbframe;
%im2uint8用于将归一化到0~1之间(im2double 处理后的图像)转换为uint8类型
writeVideo(vidOut,im2uint8(output));
nLevels = size(pind,1);
for i=startIndex+1:endIndex
%progmeter里没有执行任何操作
progmeter(i-startIndex,endIndex - startIndex + 1);
%从第二帧重复第一帧的操作,读取帧数据,frame2im将电影帧转换为索引图像,归一化,将TrueColor图像rgb转换为等效的ntsc图像yiq
temp.cdata = read(vid, i);
[rgbframe,~] = frame2im(temp);
rgbframe = im2double(rgbframe);
frame = rgb2ntsc(rgbframe);
%构造三通道拉普拉斯金字塔
[pyr(:,1),~] = buildLpyr(frame(:,:,1),'auto');
[pyr(:,2),~] = buildLpyr(frame(:,:,2),'auto');
[pyr(:,3),~] = buildLpyr(frame(:,:,3),'auto');
% temporal filtering
% 等号右边的lowpass1,lowpass2是第一帧的拉普拉斯金字塔,pyr是第二帧的拉普拉斯金字塔
lowpass1 = (1-r1)*lowpass1 + r1*pyr;
lowpass2 = (1-r2)*lowpass2 + r2*pyr;
filtered = (lowpass1 - lowpass2);
%% amplify each spatial frequency bands according to Figure 6 of our paper
ind = size(pyr,1);
delta = lambda_c/8/(1+alpha);
% the factor to boost alpha above the bound we have in the
% paper. (for better visualization)
exaggeration_factor = 2;
% compute the representative wavelength lambda for the lowest spatial
% freqency band of Laplacian pyramid
%计算拉普拉斯金字塔最低空间频率带的代表波长lambda
%第一帧不放大 从第二帧开始放大 空间波长等于对角线长度除以3?
lambda = (vidHeight^2 + vidWidth^2).^0.5/3; % 3 is experimental constant
%注意不要把l和1弄混
for l = nLevels:-1:1
indices = 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) % ignore the highest and lowest frequency band
filtered(indices,:) = 0;
elseif (currAlpha > alpha) % representative lambda exceeds lambda_c
filtered(indices,:) = alpha*filtered(indices,:);
else
filtered(indices,:) = currAlpha*filtered(indices,:);
end
ind = ind - prod(pind(l,:));
% go one level down on pyramid,
% representative lambda will reduce by factor of 2
%拉普拉斯金子塔每下降一层,lambda就折一半
lambda = lambda/2;
end
%% Render on the input video
output = zeros(size(frame));
output(:,:,1) = reconLpyr(filtered(:,1),pind);
output(:,:,2) = reconLpyr(filtered(:,2),pind);
output(:,:,3) = reconLpyr(filtered(:,3),pind);
%抑制颜色通道,相对的就放大了亮度通道
output(:,:,2) = output(:,:,2)*chromAttenuation;
output(:,:,3) = output(:,:,3)*chromAttenuation;
%放大的视频加上原始的视频
output = frame + output;
%转RGB颜色模型
output = ntsc2rgb(output);
%filtered = rgbframe + filtered.*mask;
%矫正一些超出归一化的值
output(output > 1) = 1;
output(output < 0) = 0;
%im2uint8用于将归一化到0~1之间(im2double 处理后的图像)转换为uint8类型
writeVideo(vidOut,im2uint8(output));%将帧写入视频,第一个参数是视频对象,第二个参数是视频的各帧
end
close(vidOut);
end
理解
首先这个函数算是欧拉放大的核心代码之一了,博主在里面加了不少的注释。
我们把该段代码再来捋一遍,先是读取视频,用fileparts找到视频的名称,用outName设定输出视频的路径名称扩展名等,这里不多说。接着读取第一帧用以获取该视频文件的第一帧,并且构造拉普拉斯金子塔。然后用一个循环结构,从第二帧开始进入滤波的操作。这里的滤波器用的是差分方程直接构造的,并没有设计什么巴特沃斯切比雪夫之类的滤波器。滤波之后就是放大处理。随着空间频率的增加,放大因子也在不断变小。至于里面放大的公式我还一知半解。期待下回解析。最后把处理好的视频帧叠加到输出视频文件里。下面的图是该代码里用的滤波器,用的是两个低通滤波器的差。