從視頻提取循環(huán)播放式GIF動畫的算法
來源:易賢網(wǎng) 閱讀:1452 次 日期:2015-04-02 14:07:43
溫馨提示:易賢網(wǎng)小編為您整理了“從視頻提取循環(huán)播放式GIF動畫的算法”,方便廣大網(wǎng)友查閱!

循環(huán)播放式的 GIF 動圖是網(wǎng)絡上一種流行的藝術形式,在Reddit有兩個專屬的論壇(r/perfectloops 和r/cinemagraphs),還有數(shù)不盡的Tumblr 頁面。

找到并提取電影中的循環(huán)片段,需要極大的注意力和耐心,電腦前的你,很可能就像下面這樣:

為了使事情變得簡單點,我寫了個Python腳本來自動完成這個任務。這篇博文解釋了算法背后的數(shù)學并提供了一些使用范例。

何時視頻片段循環(huán)是無縫銜接?

如果一段視頻的第一幀和最后一幀很相似,我們就說它循環(huán)起來是無縫銜接的。視頻的一幀(F) 可以表示為一個整數(shù)序列,數(shù)值表明了圖片像素的顏色。例如,(F[1]) , (F[2]) 和(F[3]) 給出了第一個像素的紅、綠、藍的數(shù)值。(F[4]) , (F[5]) , (F[6]) 給出了第二個像素的值,等等。

給定同一視頻的兩幀(F_1) , (F_2) , 我們定義它們的差別為顏色差別的和:

如果(d(F_1,F_2)) 小于某個任意閾值(T) ,我們就認為這兩幀相似。

要理解接下來的內(nèi)容,很重要的一點是注意到(d(F_1, F_2)) 定義了兩幀的距離,可以看作是平面上兩點幾何距離的一般化。

因此(d(F_1, F_2)) 有很好的數(shù)學性質(zhì),在下一節(jié)我們利用它來加快計算。

找到無縫銜接循環(huán)的片段

在這一節(jié),我們想要找到給定視頻中所有無縫銜接循環(huán)、3秒鐘內(nèi)的片段的時間(起始時間和終止時間)。有一種簡單的做法,比較每一幀和之前3秒的所有幀。當我們發(fā)現(xiàn)相似的兩幀時(即距離小于某個預設的閾值(T) ),就把對應的時間加到列表中。

問題是這種方法需要大量的比較(對于一部標準視頻大約一千萬),耗時以小時計。因此我們來看看使計算更快的一些技巧。

技巧一:使用縮減版的幀。HD高清視頻幀有數(shù)百萬的像素,因此計算它們之間的距離要數(shù)百萬次操作。當把幀縮小成縮略圖(150像素寬)時,對于我們的目的它們?nèi)匀蛔銐蚓?,距離計算卻可以快得多(內(nèi)存占用也更少)。

技巧二:利用三角不等式。有這個非常高效的技巧,在不計算兩幀距離的情況下,我們就能推斷出它們是否匹配。因為(d(F_1, F_2)) 定義了兩幀之間的數(shù)學距離,很多經(jīng)典幾何的結(jié)論都能用得上,特別是下面的關于三角形邊的長度的不等式:

第一個不等式告訴我們?nèi)绻鸄接近于B,且B接近于C,那么A接近于C。對于視頻幀來說就是:

在實際中我們這么來利用這個不等式:如果我們已知幀(F1) 和幀(F2) 很相似,(F2) 和另一幀(F3) 很相似,那么我們不用計算(d(F_1, F_3)) 就知道(F1) 和(F3) 很相似。

第二個不等式告訴我們?nèi)绻cA接近于B,而B遠離C,那么A也遠離C。對于幀來說就是:

如果(F1) 和(F2) 很相似,而(F2) 和(F3) 大不相同,那么我們無需計算(d(F_1, F_3)) 就知道(F1) 和(F3) 也有很大差別。

下面就開始有點復雜了:我們應用這些三角不等式來獲得幀距離上界和下界的信息, 在每次計算兩幀距離時這些都會得到更新。比如,計算(d(F_1, F_2)) 后,(d(F_1, F_3)) 的上下界(分別用和表示)可以如下更新:

如果更新后,,我們可以下結(jié)論:(F_1) 和(F_3) 匹配得很好。如果在某個時刻,,我們就知道(F_1) 和(F_3) 不匹配。如果用這個技術不能決定(F_1) 和(F_3) 是否匹配,那我們最終需要計算(d(F_1, F_3)) ,但知道(d(F_1, F_3)) 后反過來可以讓我們更新另一個距離((d(F_1, F_4)) )的界,以此類推。

作為說明,假設一部視頻依次有如下幀:

當算法計算到(F_4) 時,它首先計算這幀和(F_3) 的距離,發(fā)現(xiàn)它們不匹配。在這個時候,算法已經(jīng)發(fā)現(xiàn)(F_3) 和(F_2) 及(F_1) 相似,因此推斷出(F_1) 和(F_2) 都不和(F_4) 匹配(當然,之前的十幾幀也是)。在實際中,這種方法能避免80%到90%的幀距離計算。

技巧三:用高效的公式計算距離。當我們用上一節(jié)的公式來計算兩幀之間的距離時,需要大約3N次操作:N次減法,N次乘法,(N-1)次加法來得到最終的和。但是(d(F_1,F_2)) 的公式也可以重寫為如下形式,也就是余弦定理:

其中我們用了如下記號:

(d(F_1,F_2)) 的這個表達式的有趣之處在于,我們先對每幀計算一次范數(shù)(||F||) ,那么對于每對(F_1) , (F_2) 只需計算就可以得到它們之間的距離。而這只需2N次操作,比之前快了50%。

對每幀計算(|F|) 的另一個好處是,對于兩幀(F_1) 和(F_2) ,我們有:

這提供了技巧二里兩幀之間距離上下界的初始值。

用偽代碼表示的最終算法??偨Y(jié)起來,我們得到了如下算法:

for each frame F1 in the movie:

F1 <- downsized( F1 )

previous_frames <- list of frames in the 3 seconds before F1

list_of_matching_couples = []

compute and store |F1|

for each frame F2 in previous_frames:

compute upper_F1_F2 and lower_F1_F2 using Eq.2

if upper_F1_F2 < T:

mark (F1, F2) as matching

add (F1, F2) to the list_of_matching_couples

if lower_F1_F2 > T:

mark (F1, F2) as non-matching

for each frame F2 in previous_frames:

if couple (F1,F2) isn't already marked matching or non-matching:

compute d(F1, F2)

for each frame F3 after F2 in previous_frames:

update upper_F1_F3 and lower_F1_F3 using Eq.1

if upper_F1_F3 < T:

mark (F1, F3) as matching

add (F1, F3) to the list_of_matching_couples

if lower_F1_F3 > T:

mark (F1, F3) as non-matching

這里是Python的實現(xiàn)。計算時間可能依賴于視頻文件的質(zhì)量,不過我嘗試的大多數(shù)電影能在大約20分鐘處理完。令人印象深刻,是吧?Eugene。

挑選有趣的片段

前一節(jié)描述的算法找到所有的幀對,包括連續(xù)的幀(通常看起來非常像)和來自靜止片段的幀(典型的是黑屏)。因此我們會得到幾十萬個視頻片段,而只有一些是真正有趣的。在提取GIF之前我們必須找到一種方式來過濾掉不想要的片段。過濾操作只需幾秒鐘,但它的成功極大地依賴于你用的過濾標準。這里是一些有效的例子:

第一幀和最后一幀必須至少相隔0.5秒。

序列中必須至少有一幀和第一幀不匹配。這個標準能夠排除靜止片段。

第一幀的起始時間必須在上一個提取的片段的起始時間的0.5秒之后。這是為了避免重復的片段(即那些起始和終止時間幾乎一樣的片段)。

我盡量不做太多限制(避免偶然過濾掉好的片段),因此通常得到大約200個GIF,其中很多只是中等有趣(眨眼之類)。最后一步是人工過濾,就像下面這樣:

使用范例

I我實現(xiàn)了這個算法,作為我的Python視頻庫MoviePy的插件。這里是一個包含很多細節(jié)的例子腳本:

from moviepy.editor import VideoFileClip

from moviepy.video.tools.cuts import FramesMatches

# Open a video file (any format should work)

clip = VideoFileClip("myvideo.avi")

# Downsize the clip to a width of 150px to speed up things

clip_small = clip.resize(width=150)

# Find all the pairs of matching frames an return their

# corresponding start and end times. Takes 15-60 minutes.

matches = FramesMatches.from_clip(clip_small, 5, 3)

# (Optional) Save the matches for later use.

# matches.save("myvideo_matches.txt")

# matches = FramesMatches.load("myvideo_matches.txt")

# Filter the scenes: keep only segments with duration >1.5 seconds,

# where the first and last frame have a per-pixel distance < 1,

# with at least one frame at a distance 2 of the first frame,

# and with >0.5 seconds between the starts of the selected segments.

selected_scenes = matches.select_scenes(match_thr=1,

min_time_span=1.5, nomatch_thr=2, time_distance=0.5)

# The final GIFs will be 450 pixels wide

clip_medium = clip.resize(width=450)

# Extract all the selected scenes as GIFs in folder "myfolder"

selected_scenes.write_gifs(clip_medium, "myfolder")

這里是我用迪士尼的《白雪公主》來試驗時得到的:

import moviepy.editor as mp

from moviepy.video.tools.cuts import FramesMatches

clip = mp.VideoFileClip("snowwhite.mp4")

scenes = FramesMatches.from_clip(clip.resize(width=120), 5, 2)

selected_scenes = scenes.select_scenes(2, 0.5, 4, 0.5)

selected_scenes.write_gifs(clip.resize(width=270), "snow_white")

(更多圖片,請查看)

這里有些GIF可以切割得更好,有些也不太有趣(太短),還有些循環(huán)片段被錯過了。我認為罪魁禍首是最后一步過濾的參數(shù),這些本可以被調(diào)整得更好。

另一個例子:最近有人在r/perfectloops發(fā)了個Youtube視頻,要求把它轉(zhuǎn)成循環(huán)播放式的 GIF。下面的腳本就做了這件事:從Youtube下載視頻,找到切割成循環(huán)播放序列的最好的時間(t1, t2),然后生成GIF:

import moviepy.editor as mpy

from moviepy.video.tools.cuts import FramesMatches

# Get the video from youtube, save it as "hamac.mp4"

mpy.download_webfile("NpxD9TZIlv8", "hamac.mp4")

clip = mpy.VideoFileClip("hamac.mp4").resize(width=200)

matches = FramesMatches.from_clip(clip, 40, 3) # loose matching

# find the best matching pair of frames > 1.5s away

best = matches.filter(lambda x: x.time_span >1.5).best()

# Write the sequence to a GIF (with speed=30% of the original)

final = clip.subclip(best.t1, best.t2).speedx(0.3)

final.write_gif("hamac.gif", fps=10)

利用MoviePy,你也可以對GIF做后期處理,加上文字:

既然你讀到了這,這有個為你準備的更高級的技巧:

輪到你了!

我在這里呈現(xiàn)的算法并不完美。對于低亮度的影片片段,它表現(xiàn)得很糟,有時輕微的攝像機移動或者背景中的運動物體都能阻止片段循環(huán)播放。雖然這些片段能被人類輕易地修正,卻很難被算法認出、處理。

因此我的腳本并未完全解決問題,制作循環(huán)播放式的 GIF 仍然是門藝術。如果你對這個算法有任何點子或評論,或者你嘗試后發(fā)現(xiàn)了電影中的有趣的循環(huán)播放,我很樂意聽到!在那之前,快樂地制作GIF吧!

更多信息請查看IT技術專欄

更多信息請查看技術文章
易賢網(wǎng)手機網(wǎng)站地址:從視頻提取循環(huán)播放式GIF動畫的算法

2025國考·省考課程試聽報名

  • 報班類型
  • 姓名
  • 手機號
  • 驗證碼
關于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡要咨詢 | 簡要咨詢須知 | 加入群交流 | 手機站點 | 投訴建議
工業(yè)和信息化部備案號:滇ICP備2023014141號-1 云南省教育廳備案號:云教ICP備0901021 滇公網(wǎng)安備53010202001879號 人力資源服務許可證:(云)人服證字(2023)第0102001523號
聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關注公眾號:hfpxwx
咨詢QQ:526150442(9:00—18:00)版權(quán)所有:易賢網(wǎng)