鏡頭邊界檢測技術簡述
介紹
作為視頻最基本的單元幀(Frame),它的本質其實就是圖片,一系列幀通過某種順序組成在一起就構成了視頻。鏡頭邊界是視頻相鄰兩幀出現了某種意義的變化,即鏡頭邊界反映了視頻內容的不連續性。這種變化反映了某些關鍵信息,通過設定不同的檢測指標,我們能夠得到這些關鍵信息的變化。因此鏡頭邊界檢測技術(Shot Bound Detection)的實質即設定一個檢測指標來獲取我們需要的關鍵信息。這種關鍵信息在不同任務中的體現使不一樣的,動作識別中我們需要能檢測動作變化的鏡頭邊界,視頻索引任務中我們需要能夠表現這個視頻整體的視頻邊界,這就有賴於人為選擇檢測算法。雖然隨着深度學習的興起,人們開始將深度學習應用在鏡頭邊界檢測上,但是傳統圖像處理方法的鏡頭邊界檢測技術仍然有着廣泛的,本文旨在對一些常見的鏡頭邊界檢測技術進行簡述。
連續幀相減法
連續幀相減法實現
一幀本質就是一張圖片,因此衡量兩幀之間變化本質就是衡量兩張圖片的區別。在KNN算法中衡量兩張圖片之間相似度就是圖片對應像素相減之和,將其累加,值最小的即最接近的兩張圖片。連續幀相減法中,我們同樣使用這個指標來評價兩張圖片的近似度,一旦區別大到一定地步則認為該兩幀是鏡頭邊界。
讀入視頻連續的三幀
我們可以清除的看到前兩幀的圖片幾乎一樣,第三幀則發生了顏色逆轉,可以認為這是該視頻的關鍵鏡頭。事實上,作為靜止系mad,這里也確實是一個小高潮。
圖像來自於bilibili,可以點擊這里查看你就是我的真物
我們使用連續幀相減法來計算彼此之間的差別,畫出他們的差距圖
def diffimage(src_1,src_2):
#將numpy的基礎類型轉化為int型否則uint8會發生溢出
src_1 = src_1.astype(np.int)
src_2 = src_2.astype(np.int)
diff = abs(src_1 - src_2)
return diff.astype(np.uint8)
計算第二張圖片與第一張圖片差值,第二張圖片與第三張圖片差值,並使用matplotlib畫出來
但是奇怪的是肉眼看不出來的圖一和圖二(實際上圖一相比圖二圖上的內容有輕微的缺少,同時相比圖二,圖一有輕微的放大),之間的差別也是相當大。
#使用np.sum取得到的diff之和
圖一與圖二之間的像素點對應差距值之和 7777617
圖二與三之間的像素點對應差距值之和 131587585
這就是連續幀相減法的一個缺陷對於運動目標的過於敏感,圖一與圖二之間是有輕微的縮放的,而圖二與圖三更多的只是顏色上的翻轉,這極大影響了連續幀相減法檢測的准確性,如何減少這些運動對幀相減法的影響?
使用均值濾波處理后使用連續幀相減法
使相對小的平移被忽略的一個直觀的方法就是每個像素點取附近的均值,使用均值濾波器能夠忽略一些無用的細節。使用通用3*3的卷積核在進行連續幀相減法查看效果。
blur_image1 = cv2.blur(image1,ksize=(3,3))
blur_image2 = cv2.blur(image2,ksize=(3,3))
blur_image3 = cv2.blur(image3,ksize=(3,3))
#省略畫子圖代碼
cross1 = diffimage(blur_image1,blur_image2)
cross2 = diffimage(blur_image3,blur_image2)
圖一與圖二之間的像素點對應差距值之和 6894882
圖二與三之間的像素點對應差距值之和 130940724
從上面的結果與一開始的對比,均值濾波確實減弱了縮放對於兩幀差距的影響,但是減弱效果並不理想,因此我們可以得出連續幀相減法的不足,對於運動物體,縮放,平移過於敏感
使用連續幀相減法檢測監控視頻
相比上例,對監控視頻使用連續幀相減法效果顯著,考慮到監控器通常監控的區域的變化幾乎不變,人的運動能夠被輕易的檢測出來,而通常檢測監控視頻中人的出現是通常的需求,我們使用連續幀相減法進行檢測。
#核心代碼即存儲上一幀圖像與現在圖像進行幀相減法
ret, lastframe = cap.read()
while(cap.isOpened()):
ret, frame = cap.read()
if not ret:
break
if np.sum(diffimage(lastframe,frame))/frame.size > 10:
#設定的閾值
cv2.imwrite(str(uuid.uuid1())+".jpg",frame)
lastframe = frame
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
使用連續幀相減法得到關鍵幀的其中一張如下(具體代碼可以參見配套代碼)
注:視頻為錄制了自己經過攝像頭的片段
直方圖相減法
黑白圖像計算直方圖差值
正如上面兩張圖片顏色的明顯變化可能是分辨關鍵幀的因素一樣,我們能否直接從顏色通道上來找尋關鍵幀呢,首先我們畫出三張圖片的灰度分布圖(為了處理方便,將圖片轉化為灰度圖)
我們計算出各灰度分布數量並將其用直方圖表示出來
def cal_s(single_chaneel_image):
height,width = single_chaneel_image.shape
color = np.array([0 for i in range(256)])
for i in range(height):
for j in range(width):
color[single_chaneel_image[i][j]]+=1
return color
我們試着畫出第一張圖灰度分布圖,繪圖代碼很簡答
plt.plot([i for i in range(256)],cal_s(gray_image))
我們可以清除的看到第一張照片的灰度分布,將三張圖的通道都畫出來進行對比
計算直方圖差之和
print("1,2 image color distribution diviation",np.sum(diffimage(cal_s(gray_image1),cal_s(gray_image2))))
print("2 3 iamge color distribution diviation",np.sum(diffimage(cal_s(gray_image1),cal_s(gray_image3))))
#第一張圖片與第二張圖片直方圖差值為 15406
#第三張圖片與第二張圖片直方圖差值為 26378
三通道圖像計算直方圖差值
單純的黑白圖像可能丟失很大一部分細節,比如第三張圖相比第一張,第二張色調發生了明顯的反轉,但轉化為黑邊圖像則不如彩圖時那么明顯.
三通道差值計算將三通道分離然后分別計算在merge在一起,代碼如下
def cal_s_rgb(image):
image = image.astype(np.int)
color = cv2.split(image)
color = list(map(cal_s,color))
return np.array(color).astype(np.uint8)
我們還是使用圖像將圖片顯示出來
print("image2 image1 color distribution diviation",np.sum(diffimage(cal_s_rgb(image1),cal_s_rgb(image2))))
print("image2 image3 color distribution diviation",np.sum(diffimage(cal_s_rgb(image3),cal_s_rgb(image2))))
#圖像1,2直方圖差值和為 54100
#圖像2,3直方圖差值和為 73832
通過對比三張圖rgb通道的分布直方圖我們發現第三張圖的b通道分布的相比1,2圖很均勻,這也是將rgb通道分別相減求和與1,2圖片的差距主要來源,但是由於r,g通道分布的相近,我們發現彩圖的三圖差距並不如黑白圖幾乎相差一倍那么明顯。
感知哈希法
感知哈希法在計算圖片相似度,音樂相似度等方面都極為常用。該算法對每張圖片使用hash算法生成一個“指紋”字符串,通過比較不同圖片的指紋來實現圖片相似度的計算。在鏡頭邊界檢測中,一旦相似度低於一定閾值,則判斷為鏡頭邊界。選取不同hash算法對與算法的成效有較大影響。
感知哈希法的實現
圖片縮放與簡化色彩
感知哈希法在一開始將圖片進行縮放,縮放的大小由使用者指定,有如下好處
1.相當於取了一定區域的特征,減少敏感性。
2.減少生成指紋大小。
3.減少計算量
同上一步一樣簡化色彩也起了抽象特征,減少計算量,存儲量的作用。
def convertImage(image):
image = cv2.resize(image,(32,32),interpolation=cv2.INTER_CUBIC)
image = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
return image
計算DCT
DCT變換是將圖像的信號轉換到頻域的一種方法,由於實現較為復雜,此處直接調用。
dct_1 = cv2.dct(cv2.dct(dct_matrix))
縮小DCT
取得到的DCT左上角8*8的矩陣作為特征,並求得其均值,凡大於均值則為1,小於則為0
dct_1 = dct_1[:8,:8]
avg = np.mean(dct_1)
reimg_list = ['0' if i > avg else '1' for i in dct_1.flatten()]
計算指紋
將求得的轉化為16進制的數字即其指紋
#fig = hex(int("".join(reimg_list),2))
fig = ""
for i in range(0,64,4):
num = hex(int("".join(img_list[i:i+4]),2))
fig += num[2:]
計算漢明距離
def hammingDist(s1, s2):
assert len(s1) == len(s2)
return sum([ch1 != ch2 for ch1, ch2 in zip(s1, s2)])
hammingDist(fig1,fig2)
總結
鏡頭邊界檢測技術本質是根據不同的指標檢測兩幀之間的區別,根據檢測指標,區別越大則表示我們需要的信息變化越大。在實際使用中應根據不同的選擇不同的邊界檢測技術。
附錄
使用上述三種方法分別對錄制的視頻進行了檢測,可以參考我使用opencv_python寫的代碼