520,用Python訂製你的《本草綱目女孩》

摘要:讓我們來用Python訂製出心儀的「本草綱目女孩」,敲出魔性的程式碼舞蹈,520,準備好心儀女孩的舞蹈影片,把這份別出心裁的禮物給TA

本文分享自華為雲社區《【雲駐共創】華為雲AI之用Python訂製我的《本草綱目女孩》》,作者:愚公搬程式碼。

前言

近日,《本草綱目》毽子操的影片刷屏網路。公司里更是多了很多劉畊宏女孩,在520到來之際特意奉獻這篇文章給大家。如果有心儀的女孩這篇文章可以幫助你哦。

歌詞:抬腿!拍腿!,側邊的肥肉咔咔掉,人魚線馬甲線我都要!劉畊宏的男孩女孩看過來。

讓我們來用Python訂製出心儀的「本草綱目女孩」,敲出魔性的程式碼舞蹈,520,準備好心儀女孩的舞蹈影片,把這份別出心裁的禮物給TA❤️。

一、華為雲ModelArts-Notebook介紹

1.華為雲ModelArts-Notebook

ModelArts集成了基於開源的Jupyter Notebook和JupyterLab,可為您提供在線的互動式開發調試工具。您無需關注安裝配置,在ModelArts管理控制台直接使用Notebook,編寫和調測模型訓練程式碼,然後基於該程式碼進行模型的訓練。

其中,ModelArts還提供了華為自研的分散式訓練加速框架MoXing,您可以在Notebook中使用MoXing編寫訓練腳本,讓您程式碼編寫更加高效、程式碼更加簡潔。

1.1 Jupyter Notebook是什麼

Jupyter Notebook是一個可以在瀏覽器中使用的互動式的計算應用程式,該應用程式的所有可見的內容,以筆記型電腦文檔表示,包括計算的輸入和輸出、解釋文本、數學、影像和對象的富媒體等表示。因此,Jupyter Notebook可以實現將程式碼、文字完美結合起來,非常適合從事機器學習、數據分析等數據科學工作的人員。

Jupyter Notebook相關文檔://docs.jupyter.org/en/latest/

1.2 JupyterLab是什麼

JupyterLab是一個互動式的開發環境,是Jupyter Notebook的下一代產品,可以使用它編寫Notebook、操作終端、編輯MarkDown文本、打開交互模式、查看csv文件及圖片等功能。

JupyterLab相關文檔://jupyterlab.readthedocs.io/en/stable/

1.3 什麼是Moxing

MoXing是華為雲ModelArts團隊自研的分散式訓練加速框架,它構建於開源的深度學習引擎TensorFlow、MXNet、PyTorch、Keras之上。相對於TensorFlow和MXNet原生API而言,MoXing API讓模型程式碼的編寫更加簡單,允許用戶只需要關心數據輸入(input_fn)和模型構建(model_fn)的程式碼,即可實現任意模型在多GPU和分散式下的高性能運行,降低了TensorFlow和MXNet的使用門檻。另外,MoXing-TensorFlow還將支援自動超參選擇和自動模型結構搜索,用戶無需關心超參和模型結構,做到模型全自動學習。

Moxing相關文檔://github.com/huaweicloud/ModelArts-Lab/tree/master/docs/moxing_api_doc

2.Python-Opencv

Python由荷蘭數學和電腦科學研究學會的吉多·范羅蘇姆於1990年代初設計,作為一門叫做ABC語言的替代品。Python提供了高效的高級數據結構,還能簡單有效地面向對象編程。Python語法和動態類型,以及解釋型語言的本質,使它成為多數平台上寫腳本和快速開發應用的程式語言,隨著版本的不斷更新和語言新功能的添加,逐漸被用於獨立的、大型項目的開發。

Python解釋器易於擴展,可以使用C語言或C++(或者其他可以通過C調用的語言)擴展新的功能和數據類型。Python也可用於可訂製化軟體中的擴展程式語言。Python豐富的標準庫,提供了適用於各個主要系統平台的源碼或機器碼。

OpenCV用C++語言編寫,它具有C++,Python,Java和MATLAB介面,並支援Windows,Linux,Android和Mac OS,OpenCV主要傾向於實時視覺應用,並在可用時利用MMX和SSE指令,如今也提供對於C#、Ch、Ruby,GO的支援。

opencv-python的github網址://pypi.org/project/opencv-python/

OpenCV官網://opencv.org/

二、本地案例實現Python訂製我的《本草綱目女孩》

1.案例實現流程

眾所周知,影片是由一幀幀影像構成,Opencv處理影片影像資訊的原理就是將影片轉為一幀幀影像,處理完影像後再轉換為影片。

用Python實現案例流程如下:

image.png

2.案例環境部署

2.1 本機環境

  • vs2022
  • anaconda(已經包括opencv和PIL)
  • python

2.2 安裝對應的anaconda包

anaconda這是一個非常常用的python包集成管理工具,其中預安裝了很多python庫,使得我們不需要去手動安裝各種的第三方庫,我們知道自己取手動安裝的過程中,很容易就會遇到一些報錯,解決起來也非常的麻煩。

anaconda官網://www.anaconda.com/products/distribution#Downloads
image.png
下載完軟體包一路點擊安裝就行了,安裝成功後會出現如下介面。
image.png
查看是否安裝成功命令:conda --version
image.png

2.3 安裝opencv-python

進入anaconda控制台輸入如下命令:

pip install opencv-python

image.png

3.案例實現程式碼

本案例的實現過程主要分為以下幾步:

1. 導入數據
2. 導入庫函數
3. 將影片轉化為影像幀
4. 對圖片幀進行ASCII碼的轉換
5. 將轉換好的圖片幀合成影片

3.1 導入數據

影片下載地址://cnnorth4-modelhub-datasets-obsfs-sfnua.obs.cn-north-4.myhuaweicloud.com/content/35d24c0e-f337-442b-935f-ef8123062d3e/QzNm9F/dataset/test_demo0510.mp4
image.png

3.2 導入庫函數

#導入Python庫
import cv2
from PIL import Image,ImageFont,ImageDraw
import os
from cv2 import VideoWriter, VideoWriter_fourcc, imread, resize

3.3 將影片轉化為影像幀

#將影片轉換為圖片存入目標文件夾

def video_to_pic(vp):
    number = 0
    
    # 判斷載入的影片是否可以打開
    if vp.isOpened():
        
        #r:布爾型 (True 或者False),代表有沒有讀取到圖片,frame:表示截取到的一幀的圖片的數據,是個三維數組
        r,frame = vp.read()
        
        #判斷文件夾是否存在,不存在的話則新建文件夾
        if not os.path.exists('cache_pic'):
            os.mkdir('cache_pic')
        os.chdir('cache_pic')
        
    else:
        r = False
        
    #遍歷影片,並將每一幀圖片寫入文件夾
    while r:
        number += 1
        cv2.imwrite(str(number)+'.jpg',frame)
        r,frame = vp.read()
        
    print('\n由影片一共生成了{}張圖片!'.format(number))
    
    # 修改當前工作目錄至主目錄
    os.chdir("..")
    return number

3.4 對圖片幀進行ASCII碼的轉換

#將圖片進行批量化處理

def star_to_char(number, save_pic_path):
    
    #判斷文件夾是否存在,不存在的話則新建文件夾
    if not os.path.exists('cache_char'):
        os.mkdir('cache_char')

    # 生成目標圖片文件的路徑列表
    img_path_list = [save_pic_path + r'/{}.jpg'.format(i) for i in range(1, number + 1)]
    task = 0

    for image_path in img_path_list:
        
        # 獲取圖片的解析度
        img_width, img_height = Image.open(image_path).size
        task += 1

        #處理圖片,並顯示處理進程
        img_to_char(image_path, img_width, img_height, task)
        
        print('{}/{} is processed.'.format(task, number))
        
    print('=======================')
    print('All pictures were processed!')
    print('=======================')
    return 0

# 將圖片轉換為灰度影像後進行ascii_char中的ASCII值輸出
# 函數輸入像素RGBA值,輸出對應的字元碼。其原理是將字元均勻地分布在整個灰度範圍內,像素灰度值落在哪個區間就對應哪個字元碼。

def get_char(r, g, b, alpha=256):
    
    #ascii_char就是字元列表,用來將不同灰度的像素進行不同字元體替換的參照。
    ascii_char = list("#RMNHQODBWGPZ*@$C&98?32I1>!:-;. ")
    
    #alpha在為0的時候便是完全透明的圖片,所以返回空
    if alpha == 0:
        return ''
    
    length = len(ascii_char)
    
    #轉為灰度圖
    #RGBA是代表Red(紅色)、Green(綠色)、Blue(藍色)和Alpha的色彩空間,Alpha通道一般用作不透明度參數
    #如果一個像素的alpha通道數值為0%,那它就是完全透明的,而數值為100%則意味著一個完全不透明的像素
    
    gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b)
    # unit = (256.0 + 1) / len(ascii_char)
    unit = 256 / len(ascii_char)
    return ascii_char[int(gray / unit)]
def img_to_char(image_path, raw_width, raw_height, task):
    width = int(raw_width / 6)
    height = int(raw_height / 15)

    # 以RGB模式打開
    im = Image.open(image_path).convert('RGB')
    im = im.resize((width, height), Image.NEAREST)

    txt = ''
    color = []
    
    #遍歷圖片的每個像素
    for i in range(height):
        for j in range(width):
            pixel = im.getpixel((j, i))

            # 將顏色加入進行索引
            color.append((pixel[0], pixel[1], pixel[2]))
            if len(pixel) == 4:
                txt += get_char(pixel[0], pixel[1], pixel[2], pixel[3])
            else:
                txt += get_char(pixel[0], pixel[1], pixel[2])
        txt += '\n'
        color.append((255255255))

    im_txt = Image.new("RGB", (raw_width, raw_height), (255255255))
    dr = ImageDraw.Draw(im_txt)
    font = ImageFont.load_default().font
    x, y = 00
    font_w, font_h = font.getsize(txt[1])
    font_h *= 1.37  # 調整字體大小
    for i in range(len(txt)):
        if (txt[i] == '\n'):
            x += font_h
            y = -font_w
        dr.text((y, x), txt[i], fill=color[i])
        y += font_w
        
    #存儲處理後的圖片至文件夾
    os.chdir('cache_char')
    im_txt.save(str(task) + '.jpg')

    #直接進入新創建的文件夾將生成的圖片直接存入文件夾中
    os.chdir("..")
    return 0

3.5 將轉換好的圖片幀合成影片

# 進度條顯示
def process_bar(percent, start_str='', end_str='', total_length=0):
    bar = ''.join("" * int(percent * total_length)) + ''
    bar = '\r' + start_str + bar.ljust(total_length) + ' {:0>4.1f}%|'.format(percent * 100) + end_str
    print(bar, end='', flush=True)

#圖片幀合成影片

def jpg_to_video(char_image_path, FPS):
    
    # 設置影片編碼器,這裡使用MP42編碼器
    video_fourcc = VideoWriter_fourcc(*"MP42")

    # 生成目標字元圖片文件的路徑列表
    char_img_path_list = [char_image_path + r'/{}.jpg'.format(i) for i in range(1, number + 1)]

    # 獲取圖片的解析度
    char_img_test = Image.open(char_img_path_list[1]).size
    if not os.path.exists('video'):
        os.mkdir('video')
    video_writter = VideoWriter('video/output.avi', video_fourcc, FPS, char_img_test)
    sum = len(char_img_path_list)
    count = 0
    for image_path in char_img_path_list:
        img = cv2.imread(image_path)
        video_writter.write(img)
        end_str = '100%'
        count = count + 1
        process_bar(count / sum, start_str='', end_str=end_str, total_length=15)

    video_writter.release()
    print('\n')
    print('=======================')
    print('The video is finished!')
    print('=======================')

3.6 主函數

if __name__ == "__main__":
    #初始影片路徑
    video_path = 'test_demo0510.mp4'

    #原始影片轉為圖片的圖片保存路徑
    save_pic_path = 'cache_pic'

    #圖片經處理後的圖片保存路徑
    save_charpic_path = 'cache_char'

    # 讀取影片
    vp = cv2.VideoCapture(video_path)

    # 將影片轉換為圖片 並進行計數,返回總共生成了多少張圖片
    number = video_to_pic(vp)

    # 計算影片幀數
    FPS = vp.get(cv2.CAP_PROP_FPS)

    # 將影像進行字元串處理後
    star_to_char(number, save_pic_path)

    vp.release()

    # 將圖片合成為影片
    jpg_to_video(save_charpic_path, FPS)

3.7 運行結果

運行後一共生成了382張圖片,和影片文件,保存在如下文件夾下。
image.png

4.案例播放程式碼

import cv2
from IPython.display import clear_output, Image, display

def show_video(video_path, show_text):
    video = cv2.VideoCapture(video_path)

    while True:
        try:
            clear_output(wait=True)
            # 讀取影片
            ret, frame = video.read()
            if not ret:
                break
            height, width, _ = frame.shape
            cv2.putText(frame, show_text, (0100), cv2.FONT_HERSHEY_TRIPLEX, 3.65, (25500), 2)
            frame = cv2.resize(frame, (int(width / 2), int(height / 2)))
            _, ret = cv2.imencode('.jpg', frame)
            
            display(Image(data=ret))
            
        except KeyboardInterrupt:
            video.release()
            
#影片循環播放
i=1
while i>0:
    show_video('video/output.avi',str(i))
    i=i+1

output_分割_gif.gif

三、華為雲AI實現Python訂製我的《本草綱目女孩》

1.基礎配置

“本草綱目”健身操字元串影片操作實例的案例頁面如下://developer.huaweicloud.com/develop/aigallery/Notebook/detail?id=c81526e5-cc88-497f-8a21-41a5632e014e

image.png

點擊Run in ModelArts,進入JupyterLab頁面。

image.png

配置當前運行環境,切換規格,並選擇免費的就好了。
image.png

2.導入數據

選擇下方程式碼,點擊運行。
image.png

運行完成後如下:
image.png

3.安裝相關依賴

選擇下方程式碼,點擊運行安裝opencv包

pip install opencv_python

image.png

4.導入庫函數

#導入Python庫
import cv2
from PIL import Image,ImageFont,ImageDraw
import os
from cv2 import VideoWriter, VideoWriter_fourcc, imread, resize

image.png

5.將影片轉化為影像幀

#將影片轉換為圖片存入目標文件夾

def video_to_pic(vp):
    number = 0
    
    # 判斷載入的影片是否可以打開
    if vp.isOpened():
        
        #r:布爾型 (True 或者False),代表有沒有讀取到圖片,frame:表示截取到的一幀的圖片的數據,是個三維數組
        r,frame = vp.read()
        
        #判斷文件夾是否存在,不存在的話則新建文件夾
        if not os.path.exists('cache_pic'):
            os.mkdir('cache_pic')
        os.chdir('cache_pic')
        
    else:
        r = False
        
    #遍歷影片,並將每一幀圖片寫入文件夾
    while r:
        number += 1
        cv2.imwrite(str(number)+'.jpg',frame)
        r,frame = vp.read()
        
    print('\n由影片一共生成了{}張圖片!'.format(number))
    
    # 修改當前工作目錄至主目錄
    os.chdir("..")
    return number

image.png

6.對圖片幀進行ASCII碼的轉換

#將圖片進行批量化處理

def star_to_char(number, save_pic_path):
    
    #判斷文件夾是否存在,不存在的話則新建文件夾
    if not os.path.exists('cache_char'):
        os.mkdir('cache_char')

    # 生成目標圖片文件的路徑列表
    img_path_list = [save_pic_path + r'/{}.jpg'.format(i) for i in range(1, number + 1)]
    task = 0

    for image_path in img_path_list:
        
        # 獲取圖片的解析度
        img_width, img_height = Image.open(image_path).size
        task += 1

        #處理圖片,並顯示處理進程
        img_to_char(image_path, img_width, img_height, task)
        
        print('{}/{} is processed.'.format(task, number))
        
    print('=======================')
    print('All pictures were processed!')
    print('=======================')
    return 0

# 將圖片轉換為灰度影像後進行ascii_char中的ASCII值輸出
# 函數輸入像素RGBA值,輸出對應的字元碼。其原理是將字元均勻地分布在整個灰度範圍內,像素灰度值落在哪個區間就對應哪個字元碼。

def get_char(r, g, b, alpha=256):
    
    #ascii_char就是字元列表,用來將不同灰度的像素進行不同字元體替換的參照。
    ascii_char = list("#RMNHQODBWGPZ*@$C&98?32I1>!:-;. ")
    
    #alpha在為0的時候便是完全透明的圖片,所以返回空
    if alpha == 0:
        return ''
    
    length = len(ascii_char)
    
    #轉為灰度圖
    #RGBA是代表Red(紅色)、Green(綠色)、Blue(藍色)和Alpha的色彩空間,Alpha通道一般用作不透明度參數
    #如果一個像素的alpha通道數值為0%,那它就是完全透明的,而數值為100%則意味著一個完全不透明的像素
    
    gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b)
    # unit = (256.0 + 1) / len(ascii_char)
    unit = 256 / len(ascii_char)
    return ascii_char[int(gray / unit)]
def img_to_char(image_path, raw_width, raw_height, task):
    width = int(raw_width / 6)
    height = int(raw_height / 15)

    # 以RGB模式打開
    im = Image.open(image_path).convert('RGB')
    im = im.resize((width, height), Image.NEAREST)

    txt = ''
    color = []
    
    #遍歷圖片的每個像素
    for i in range(height):
        for j in range(width):
            pixel = im.getpixel((j, i))

            # 將顏色加入進行索引
            color.append((pixel[0], pixel[1], pixel[2]))
            if len(pixel) == 4:
                txt += get_char(pixel[0], pixel[1], pixel[2], pixel[3])
            else:
                txt += get_char(pixel[0], pixel[1], pixel[2])
        txt += '\n'
        color.append((255255255))

    im_txt = Image.new("RGB", (raw_width, raw_height), (255255255))
    dr = ImageDraw.Draw(im_txt)
    font = ImageFont.load_default().font
    x, y = 00
    font_w, font_h = font.getsize(txt[1])
    font_h *= 1.37  # 調整字體大小
    for i in range(len(txt)):
        if (txt[i] == '\n'):
            x += font_h
            y = -font_w
        dr.text((y, x), txt[i], fill=color[i])
        y += font_w
        
    #存儲處理後的圖片至文件夾
    os.chdir('cache_char')
    im_txt.save(str(task) + '.jpg')

    #直接進入新創建的文件夾將生成的圖片直接存入文件夾中
    os.chdir("..")
    return 0

image.png

7.將轉換好的圖片幀合成影片

# 進度條顯示
def process_bar(percent, start_str='', end_str='', total_length=0):
    bar = ''.join("" * int(percent * total_length)) + ''
    bar = '\r' + start_str + bar.ljust(total_length) + ' {:0>4.1f}%|'.format(percent * 100) + end_str
    print(bar, end='', flush=True)

#圖片幀合成影片

def jpg_to_video(char_image_path, FPS):
    
    # 設置影片編碼器,這裡使用MP42編碼器
    video_fourcc = VideoWriter_fourcc(*"MP42")

    # 生成目標字元圖片文件的路徑列表
    char_img_path_list = [char_image_path + r'/{}.jpg'.format(i) for i in range(1, number + 1)]

    # 獲取圖片的解析度
    char_img_test = Image.open(char_img_path_list[1]).size
    if not os.path.exists('video'):
        os.mkdir('video')
    video_writter = VideoWriter('video/output.avi', video_fourcc, FPS, char_img_test)
    sum = len(char_img_path_list)
    count = 0
    for image_path in char_img_path_list:
        img = cv2.imread(image_path)
        video_writter.write(img)
        end_str = '100%'
        count = count + 1
        process_bar(count / sum, start_str='', end_str=end_str, total_length=15)

    video_writter.release()
    print('\n')
    print('=======================')
    print('The video is finished!')
    print('=======================')

image.png

8.運行主函數

if __name__ == "__main__":
    #初始影片路徑
    video_path = 'test_demo0510.mp4'

    #原始影片轉為圖片的圖片保存路徑
    save_pic_path = 'cache_pic'

    #圖片經處理後的圖片保存路徑
    save_charpic_path = 'cache_char'

    # 讀取影片
    vp = cv2.VideoCapture(video_path)

    # 將影片轉換為圖片 並進行計數,返回總共生成了多少張圖片
    number = video_to_pic(vp)

    # 計算影片幀數
    FPS = vp.get(cv2.CAP_PROP_FPS)

    # 將影像進行字元串處理後
    star_to_char(number, save_pic_path)

    vp.release()

    # 將圖片合成為影片
    jpg_to_video(save_charpic_path, FPS)

image.png

等待主函數執行完成,後會生成如下三個文件夾:
image.png

9.播放影片

image.png

總結

本文主要介紹了本地開發和華為雲ModelArts開發兩種形式,從開發流程中大家也明白那種形式開發更簡單。

ModelArts是面向開發者的一站式AI開發平台,為機器學習與深度學習提供海量數據預處理及半自動化標註、大規模分散式Training、自動化模型生成,及端-邊-雲模型按需部署能力,幫助用戶快速創建和部署模型,管理全周期AI工作流。

華為雲ModelArts-Notebook雲開發的優勢不需要本地進行安裝資源包,在ModelArts-Notebook環境就已經集成了這些環境,減少了人為部署壓力,更易於上手、更高性能、一站式服務、支援多種主流框架。


本文整理自華為雲社區【內容共創】活動第16期。查看活動詳情://bbs.huaweicloud.com/blogs/352652

 

點擊關注,第一時間了解華為雲新鮮技術~