首頁 > 軟體

Python實現藍線挑戰特效的範例程式碼

2022-10-12 14:00:48

在抖音曾經火了一陣子的藍線挑戰特效,其原理很簡單:在藍線經過後保留本幀的部分畫素,形成蒙板圖片,未經過處照常切換幀圖片,再將蒙版圖片貼到每幀圖片上。本著我上我也行的想法,試著用opencv-python實現這個效果,做了攝像頭版本和視訊處理版本。

圖源:抖音

圖源: PPT

1. 攝像頭版本       

從上述描述可知,在攝像頭版本中可規定每幀取固定寬度畫素,如2個畫素,假設視訊尺寸為640*480,則需要480/2=240幀,若視訊影格率(每秒的幀數)為30,則執行8秒,實際受計算速度等影響會略大於這個值,以下為關鍵部位程式碼:

(1)從攝像頭獲取每幀影象

video = CV2.VideoCapture(0, CV2.CAP_DSHOW)
ret, frame = video.read()    # frame為np陣列,寬100高200時,陣列形狀為200 * 100 *3
frame = CV2.flip(frame,1) # 左右翻轉影象為映象

(2)製作蒙版圖片,並取每幀的固定數量的畫素

#通過row_index記錄當前的行索引,獲取畫素作為蒙版圖片
canvas[row_index:row_index + pixel_number_each_frame] = frame[row_index:row_index + pixel_number_each_frame]
row_index += pixel_number_each_frame    # 每次執行增加固定畫素寬度
if row_index + width_blueline < hight:    # 避免因為增加固定畫素,導致超出影象的高度
    frame[:row_index] = canvas[:row_index]    # 將每幀的影象上部替換為蒙版圖片
    frame[row_index:row_index+ width_blueline] = array_blueline    # 新增藍線矩陣
   # 視窗顯示,BUG在於frame資料為浮點數時預設RGB數值範圍0~1,當為整數時為0~255
    CV2.imshow('Viewer', frame / 255)

(3)將處理完的圖片及時儲存,便於後期匯出視訊

CV2.imwrite(f'{output_frame_dirpath}/{count}.jpg', frame)

(4)合成視訊

def img_to_video(output_video_path, frame_dirpath, fps):
    """
    將處理好的幀圖片合成視訊
    :param output_video_path: 輸出視訊的地址
    :param frame_dirpath: 幀圖片所在資料夾地址
    :param fps: 輸出影格率
    :return: None
    """
    img = CV2.imread(f"{frame_dirpath}/1.jpg")
    hight, width, _ = img.shape
    fourcc = CV2.VideoWriter_fourcc(*'mp4v')
    videoWriter = CV2.VideoWriter(output_video_path, fourcc, fps, (width, hight))
    order = [int(i.strip(".jpg")) for i in os.listdir(frame_dirpath) if                         i.endswith(".jpg")]
    jpglist = [f"{frame_dirpath}/{i}.jpg" for i in sorted(order)]
    for i, jpg in enumerate(jpglist):
        img = CV2.imread(filename=jpg)
        videoWriter.write(img)
        print(f"將字元畫寫入視訊, 進度{(i + 1)}/{len(jpglist)}!")
    videoWriter.release()
    print(f"{output_video_path} 輸出完成!")

2. 視訊處理版本

與攝像頭版本不同,視訊版本需要獲取視訊資訊以做處理。

(1)將視訊抽幀為圖片

def video_to_img(frame_dirpath, video_path):
    """
    將視訊抽取為幀圖片以便處理
    :param frame_dirpath: 存放抽取好的幀圖片資料夾地址
    :param video_path: 視訊地址
    :return: None
    """
    vc = CV2.VideoCapture(video_path)
    c = 0
    ret = vc.isOpened()
    while ret:
        c += 1
        ret, frame = vc.read()
        if ret:
            CV2.imwrite(f'{frame_dirpath}/{c}.jpg', frame)
            print(f'生成{frame_dirpath}/{c}.jpg')
        else:
            break
    vc.release()
    print("視訊按各幀提取完成!")

(2)獲取視訊基本資訊

def get_video_msg(video_path):
    """
    獲取視訊的基本資訊
    :param video_path: 視訊地址
    :return: [幀數量,[寬度,高度],影格率]
    """
    cap = CV2.VideoCapture(video_path)
    if cap.isOpened():
        frame_number = cap.get(7)
        width = cap.get(3)
        hight = cap.get(4)
        fps = cap.get(5)
        return [frame_number, [width, hight], fps]
    return [-1, -1, -1, [-1, -1], -1]

(3)計算相關引數。新視訊的時長即為掃描時長,即每幀抽取畫素= 圖片高度 / 總幀數,此時需要取整,且取整誤差=圖片高度 - 每幀抽取畫素* 總幀數,不處理會導致藍線無法在時長內掃描完整個高度。

array_blueline = np.array([[[255, 255, 0] for _ in range(width)] for _ in                   range(width_blueline)])
pixel_number_each_frame = int(hight / frame_number) # 每次取幀擷取的畫素範圍
err = hight - pixel_number_each_frame * frame_number - 3 # 誤差值分散到每幀,留3個畫素給藍線

(4)將誤差分散到較前的幀圖片中

if err_count < err:
    canvas[row_index:row_index + pixel_number_each_frame + 1] = img[row_index:row_index + pixel_number_each_frame + 1]
  row_index += pixel_number_each_frame + 1
  err_count += 1    # 計算誤差部分是否使用完
else:
   canvas[row_index:row_index + pixel_number_each_frame] = img[row_index:row_index + pixel_number_each_frame]
  row_index += pixel_number_each_frame
  if row_index + width_blueline <= hight:    # 避免索引跑出圖片範圍而報錯
           img[:row_index] = canvas[:row_index]
           img[row_index:row_index+ width_blueline] = array_blueline

(5)將圖片重新合成視訊,同攝像頭版本,不再贅述

到此這篇關於Python實現藍線挑戰特效的範例程式碼的文章就介紹到這了,更多相關Python藍線挑戰特效內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com