方格精選

Python Pillow 套件-製作GIF動畫

更新於 發佈於 閱讀時間約 20 分鐘

圖像互換格式(Graphics Interchange Format, GIF)是一種媒體格式,可以將多偵的圖像組合成一個連續的動畫。無論是在社交媒體上分享有趣的動畫,還是在網站上展示產品的動態效果,GIF都是一個絕佳的選擇。

上一篇文章說明了Pillow套件的基礎操作,例如圖像的縮放,裁減,旋轉、合併等等,而這篇文章,將介紹如何使用上一篇提到的技巧,製作出流暢的GIF動畫。

Pillow將檔案儲存成GIF格式的函式與上一章介紹的一樣,是使用 Image.save() ,而 save​有多個參數可以控制GIF的屬性,以下是常見的參數:

image.save(fp: str, 
           save_all: bool,
           append_images: list,
           optimize: bool,
           duration: int | list,
           loop: int)
  1. image :GIF第一幀的圖。
  2. save_all:是否保存多幀GIF。
  3. append_images :要追加為GIF的其他圖像串列。
  4. duration:每幀的持續時間,以毫秒為單位。可以是單一整數(對所有幀使用相同的持續時間)或整數串列(每幀的持續時間)。
  5. loop:GIF的播放次數。0表示無限循環,1表示播放一次,2表示播放兩次,依此類推

範例1-使用繪製製作動畫

先來看圖像:

square_animation.gif

square_animation.gif

這是一個200x200的圖像,中心有一個紅色矩形,從100x100,每幀邊長各增長10像素,直到填滿200x200的空間,每幀的持續時間為100ms,並且無限循環。

在說明如何製作之前,先來介紹一個可以生成新圖像的函式:

Image.new(mode: str, size: tuple | str, color)
  1. mode:圖像的顏色模式。常見的模式有:
    • 'RGB': 三通道,紅色、綠色和藍色。
    • 'RGBA': 四通道,紅色、綠色、藍色和透明度/alpha。
    • 'L': 灰度模式。
    • '1': 二值模式,只有黑和白。
  2. size:圖像的尺寸,表示為(width, height)
  3. color:背景顏色。對於'RGB'模式,這將是一個三元組,例如(255, 255, 255)表示白色,也可以使用顏色的英文名,如 'white' 。對於'L'模式,這將是一個介於0(黑色)和255(白色)之間的整數。

例如生成一個100x100,背景顏色為藍色的圖像:

from PIL import Image
img = Image.new('RGB', (100, 100), (0, 0, 255))
img.save(r'blue_square.png')
blue.png

blue.png

介紹完 new()之後,就可以開始介紹如何製作了。

製作過程

首先,我們需要一個200x200、背景為透明的圖像:

from PIL import Image, ImageDraw
img = Image.new('RGBA', (200, 200), (255, 255, 255, 0))

接著,在圖像的中間繪製100x100的紅色矩形:

draw = ImageDraw.Draw(img)
upper_left = (100 - 50, 100 - 50)
lower_right = (100 + 50, 100 + 50)
draw.rectangle([upper_left, lower_right], fill='red')
img.save(r'red_square.png')
red_square.png

red_square.png

接下來,因為每次邊長都會增加十,也就是說,下一張圖像的矩形大小會是110x110,為了維持在圖像中央,矩形左上角的點座標會是(100 - 55, 100 - 500),右下地的點座標則是(100 + 55, 100 + 55),依此類推,下一次就是(100 - 60, 100 - 60)與(100 + 60, 100 + 60),直到(0, 0)與(200, 200)時,紅色矩形填滿整個圖像。我們利用 for 迴圈來繪製每一張圖像,並且將圖像儲存至 images​ 的列表內,程式碼如下:

# 創建一個圖像列表
images = []
for i in range(100, 201, 10):
    # 創建一個200x200的透明背景
    img = Image.new('RGBA', (200, 200), (255, 255, 255, 0))
   
    # 在正中間繪製紅色矩形
    draw = ImageDraw.Draw(img)
    upper_left = (100 - i//2, 100 - i//2)
    lower_right = (100 + i//2, 100 + i//2)
    draw.rectangle([upper_left, lower_right], fill='red')
   
    images.append(img)

接下來,我們只要將圖像利用 save 輸出成GIF就完成了,第一幀圖像是 images列表的第一個元素,因此使用 images[0].save() 來製作GIF,按照上面的格式依序填入參數:

images[0].save(r'square_animation.gif',
                save_all = True,
                append_images = images[1:],
                optimize = False,
                duration = 100,
                loop = 0)

值得注意的是 append_images = images[1:],使用列表分割使得 images[0] 不會出現兩幀。以下是完整的程式碼:

from PIL import Image, ImageDraw

# 創建一個圖像列表
images = []
for i in range(100, 201, 10):
    # 創建一個200x200的透明背景
    img = Image.new('RGBA', (200, 200), (255, 255, 255, 0))
   
    # 在正中間繪製紅色正方形
    draw = ImageDraw.Draw(img)
    upper_left = (100 - i//2, 100 - i//2)
    lower_right = (100 + i//2, 100 + i//2)
    draw.rectangle([upper_left, lower_right], fill='red')
   
    images.append(img)

# 將圖像列表保存為GIF
images[0].save(r'square_animation.gif',
               save_all = True,
               append_images = images[1:],
               optimize = False,
               duration = 100,
               loop = 0)

範例二-圖像旋轉

接下來用上一篇文章提到的 rotate() 製作如下動畫:

rotated_animation,gif

rotated_animation,gif


這是一個100x100的水藍色矩形,以逆時針每次旋轉10度,無限循環,每幀的持續時間為100ms。

首先一樣使用 new() 來製作一個100x100的水藍色圖像:


from PIL import Image
base_img = Image.new('RGB', (100, 100), color = 'aqua')
base_img.save(r'aqua.png')
aqua.png

aqua.png

來看看轉五十度的圖像:

rotated.png

rotated.png

旋轉成功,那我們就可以使用 for 迴圈來快速製作旋轉10度到350度的圖像,由於旋轉0度等於360度, saveloop 參數會自動幫我們回到第一幀圖像,因此我們 for 迴圈只要到 350 就行了。

rotated_images = []
# 旋轉圖像
for angle in range(10, 360, 10):
    rotated_images.append(base_img.rotate(angle))

將圖像列表合併成GIF動畫:

# 將旋轉的圖像保存為GIF
rotated_images[0].save(r'rotated_animation.gif',
                      save_all = True,
                      append_images = rotated_images[1:],
                      optimize = False,
                      duration = 100,
                      loop = 0)

製作旋轉圖像也可以使用Python的生成式,完整程式碼如下:

from PIL import Image
# 創建一個水藍色圖像
base_img = Image.new('RGB', (100, 100), color = 'aqua')

# 旋轉圖像
rotated_images = [base_img.rotate(angle) for angle in range(0, 360, 10)]

# 將旋轉的圖像保存為GIF
rotated_images[0].save(r'rotated_animation.gif',
                      save_all = True,
                      append_images = rotated_images[1:],
                      optimize = False,
                      duration = 100,
                      loop = 0)

範例三-圖像裁剪

現在有一張風景圖:

landscape.png

landscape.png

我們希望能擁有以下效果:

sliding_window.gif

sliding_window.gif

這張gif的大小為640x300,每幀擷取的部份的會往下5像素,到最底後會回彈,並以每幀往上5巷素的方式移動,直到回到圖像的最頂端,無限循環,每幀的持續時間為50ms。

一開始,我們先讀入 landscape.png,並顯示其大小:

from PIL import Image
img = Image.open(r'landscape.png')
width, height = img.size
print(f'大小:{width}x{height}')
大小:640x640

先擷取(0, 0)到(640, 300)的範圍:

cropped = img.crop((0, 0, 640, 300))
cropped.save(r'cropped.png')
cropped.png

cropped.png

接著只要一路以每幀5像素的速度向下裁剪,直到碰到底部,也就是裁剪到(0, 340, 640, 640):

for step in range(0, height - 300 + 1, 5):
    box = (0, 0 + step, width, 300 + step)
    cropped = img.crop(box)
    images.append(cropped)

由於碰到底部要回彈,也就是將剛剛的過程反著再來一遍,我們可以直接複製 images內倒數第二個元素到第二個的元素,也就是去掉頭尾,然後連接到原本 images的後面,程式碼如下:

images += images[-2:0:-1]

接著將圖像列表轉為GIF並儲存:

images[0].save(r'example3\sliding_window.gif',
                save_all = True,
                append_images = images[1:],
                optimize = False,
                duration = 50,
                loop = 0)

完整程式碼如下:

from PIL import Image

img = Image.open(r'landscape.png')
images = []
width, height = img.size

for step in range(0, height - 300 + 1, 5):
    box = (0, 0 + step, width, 300 + step)
    cropped = img.crop(box)
    images.append(cropped)

images += images[-2:0:-1]

images[0].save(r'sliding_window.gif',
                save_all = True,
                append_images = images[1:],
                optimize = False,
                duration = 50,
                loop = 0)

範例四-圖像合併

有三張風景圖:

0.png

0.png

1.png

1.png

2.png

2.png

我們可以直接使用 blend()來達成如下效果:

blend.gif

blend.gif

利用Python的生成式將圖像依序讀入並存於 images列表中 :

from PIL import Image

images = [Image.open(f'{img}.png') for img in range(3)]

images列表存為GIF:

images[0].save(r'blend.gif', 
               save_all = True,
               append_images = images[1:],
               optimize = False,
               duration = 500,
               loop = 0)

但這樣轉場過於單調,我們可以透過調整 alpha參數來製作淡出的效果,淡出的過程每幀持續時間100ms,每次淡出圖像的透明度增加10%,圖像每幀持續時間300​ms:

fade_out.gif

fade_out.gif

一樣先讀入圖像,不過為了等等製作淡出動畫方便, base_images的最後我們再增加一張開頭的圖像:

from PIL import Image

base_images = [Image.open(f'{img}.png') for img in range(3)]
base_images.append(base_images[0])
images = []

先將目前顯示的圖像加入到 images中,接著開始製作淡出動畫,淡出圖像的透明度每幀增加10%:

for i in range(len(base_images) - 1):
    # 將目前圖像加入至images
    images.append(base_images[i])

    # 每幀淡出圖像的透明度增加10%
    for alpha in range(10):
        blend = Image.blend(base_images[i], base_images[i + 1], alpha = alpha / 10)
        images.append(blend)

淡出過程有10幀圖像,這個過程每幀100ms,而顯示完整圖像時每幀300ms,完整圖像分別位於 images[0]images[11]images[22],也就是說,當索引值是11的倍數時,持續時間須為300ms,剩下的每幀持續時間為100ms,我們利用以下程式碼製作 durations陣列:

for i in range(len(images)):
    if i % 11:
        durtions.append(100)
   
    else:
        durtions.append(300)

最後將圖像列表儲存為GIF就成功啦:

images[0].save(r'example4\fade_out.gif', 
               save_all = True,
               append_images = images[1:],
               optimize = False,
               duration = durtions,
               loop = 0)

完整程式碼如下:

from PIL import Image

base_images = [Image.open(f'example4\\{img}.png') for img in range(3)]
base_images.append(base_images[0])
images = []

for i in range(len(base_images) - 1):
    # 將目前圖像加入至images
    images.append(base_images[i])

    # 每幀淡出圖像的透明度增加10%
    for alpha in range(10):
        blend = Image.blend(base_images[i], base_images[i + 1], alpha = alpha / 10)
        images.append(blend)

durtions = []

for i in range(len(images)):
    if i % 11:
        durtions.append(100)
   
    else:
        durtions.append(300)

images[0].save(r'example4\fade_out.gif',
               save_all = True,
               append_images = images[1:],
               optimize = False,
               duration = durtions,
               loop = 0)

以上就是一些利用Pillow套件製作gif的範例,範例程式碼與圖片可以在此查看與下載:YukiHataRin/vocus_gif (github.com)。感謝大家的閱讀!

avatar-img
13會員
4內容數
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
HataRin的沙龍 的其他內容
在當今的數位時代,圖像處理已成為許多應用和項目的核心部分,從網站設計到機器學習,高效且靈活的圖像處理工具變得越來越重要。本文將介紹一個好用的Python套件-Pillow,Pillow套件是Python Imaging Library(PIL)的一個分支雖,然它不像Photoshop等軟體一樣強大,
在當今的數位時代,圖像處理已成為許多應用和項目的核心部分,從網站設計到機器學習,高效且靈活的圖像處理工具變得越來越重要。本文將介紹一個好用的Python套件-Pillow,Pillow套件是Python Imaging Library(PIL)的一個分支雖,然它不像Photoshop等軟體一樣強大,
你可能也想看
Google News 追蹤
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
PixVerse是一款AI工具,推出了全新的〝魔術刷〞功能,讓使用者能夠輕鬆生成動態圖像。同時,PixVerse每天還會贈送免費的20積分,歡迎透過本文提供的連結前往官方網站體驗。
Thumbnail
GIFShift 是一個免費的 AI GIF 轉換工具,可以將 GIF 圖片轉換為動漫風格,支援輸入提示改變圖像內容,例如指定英雄電影主角來生成人物,用情緒描述更換臉部表情。無須註冊、完全免費,提供 GIPHY 搜尋功能,方便尋找素材創作新的 GIF 梗圖。
Thumbnail
Python語法包括條件語句、迴圈、函數和變數的使用。條件語句如if、elif和else用於進行條件判斷,for和while是兩種主要的迴圈,def用於定義函數。變數可以被賦予數字或字符串,並可使用類型提示來指定變數的類型。註解可以是單行或多行,並可用於解釋函數或類的用途和作用。
呼!折騰了好久,終於徹底搞清楚pygame的各個blend mode所用的計算式,到底是長啥樣子了。
Thumbnail
當我們在進行影像處理時, 在Python的世界最常聽到的就是OpenCV, 而我們在處理影片時也會想要僅針對某時間段的影片進行處理, 今天我們就來教您如何透過OpenCV來讀取特定的時間區段。 在進入主題之前, 有一些基本概念務必先行建立, 一個影片是由多張圖片組成的, 因此最小單元為一張圖
Thumbnail
涉及圖像處理和計算機視覺時,色彩空間轉換是一個常見操作,應用如下: 降維: 將一張彩色圖像轉換為灰度圖像可以減少數據的維度,簡化處理過程,同時在某些情況下保留重要的視覺信息。 突顯特徵: 在某些情況下,某些色彩通道可能包含冗餘或不必要的信息,通過轉換到其他色彩空間,可以更好地突顯圖像中的重要特徵
Thumbnail
Pixverse 是一個免費的 AI 影片生成器,它可以根據您輸入的文字或圖片,生成各種風格和主題的高質量的影片,它有分成網頁版和 Discord 版,今天要介紹的是網頁版的 Pixverse,介面簡單好操作,只需要在輸入框輸入您的想法,選擇相關設定,就可以生成相應的影片,更棒的是,還可以商用呢!
Thumbnail
本文將介紹影像的基本操作包括:影像的讀取、顯示、保存,以及一些常見的操作如裁剪、旋轉、縮放等。 語法介紹 讀取影像: cv2.imread函數的參數是影像的檔案路徑。讀取後的影像以NumPy的ndarray形式表示。
Thumbnail
這篇文章主要討論了在 iOS 開發中,使用 UIImagePickerController 來選取 GIF 圖片時會變成靜態截圖的問題,並推薦了使用新的 PHPickerViewController 選取 GIF 檔案。同時提供了一些處理 GIF 圖片的方法。
Thumbnail
本篇文章介紹了在Discord上操作Pika Labs,讓你能快速地體驗到生成影片的樂趣。透過Discord加入Pika頻道,你可以輕鬆生成影片,並控制影片呈現方式。文章還提供了在Discord生成影片的指令,文字生成圖片和圖片生成影片的步驟。最後,文章總結了在Discord與官網生成影片的差異。
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
PixVerse是一款AI工具,推出了全新的〝魔術刷〞功能,讓使用者能夠輕鬆生成動態圖像。同時,PixVerse每天還會贈送免費的20積分,歡迎透過本文提供的連結前往官方網站體驗。
Thumbnail
GIFShift 是一個免費的 AI GIF 轉換工具,可以將 GIF 圖片轉換為動漫風格,支援輸入提示改變圖像內容,例如指定英雄電影主角來生成人物,用情緒描述更換臉部表情。無須註冊、完全免費,提供 GIPHY 搜尋功能,方便尋找素材創作新的 GIF 梗圖。
Thumbnail
Python語法包括條件語句、迴圈、函數和變數的使用。條件語句如if、elif和else用於進行條件判斷,for和while是兩種主要的迴圈,def用於定義函數。變數可以被賦予數字或字符串,並可使用類型提示來指定變數的類型。註解可以是單行或多行,並可用於解釋函數或類的用途和作用。
呼!折騰了好久,終於徹底搞清楚pygame的各個blend mode所用的計算式,到底是長啥樣子了。
Thumbnail
當我們在進行影像處理時, 在Python的世界最常聽到的就是OpenCV, 而我們在處理影片時也會想要僅針對某時間段的影片進行處理, 今天我們就來教您如何透過OpenCV來讀取特定的時間區段。 在進入主題之前, 有一些基本概念務必先行建立, 一個影片是由多張圖片組成的, 因此最小單元為一張圖
Thumbnail
涉及圖像處理和計算機視覺時,色彩空間轉換是一個常見操作,應用如下: 降維: 將一張彩色圖像轉換為灰度圖像可以減少數據的維度,簡化處理過程,同時在某些情況下保留重要的視覺信息。 突顯特徵: 在某些情況下,某些色彩通道可能包含冗餘或不必要的信息,通過轉換到其他色彩空間,可以更好地突顯圖像中的重要特徵
Thumbnail
Pixverse 是一個免費的 AI 影片生成器,它可以根據您輸入的文字或圖片,生成各種風格和主題的高質量的影片,它有分成網頁版和 Discord 版,今天要介紹的是網頁版的 Pixverse,介面簡單好操作,只需要在輸入框輸入您的想法,選擇相關設定,就可以生成相應的影片,更棒的是,還可以商用呢!
Thumbnail
本文將介紹影像的基本操作包括:影像的讀取、顯示、保存,以及一些常見的操作如裁剪、旋轉、縮放等。 語法介紹 讀取影像: cv2.imread函數的參數是影像的檔案路徑。讀取後的影像以NumPy的ndarray形式表示。
Thumbnail
這篇文章主要討論了在 iOS 開發中,使用 UIImagePickerController 來選取 GIF 圖片時會變成靜態截圖的問題,並推薦了使用新的 PHPickerViewController 選取 GIF 檔案。同時提供了一些處理 GIF 圖片的方法。
Thumbnail
本篇文章介紹了在Discord上操作Pika Labs,讓你能快速地體驗到生成影片的樂趣。透過Discord加入Pika頻道,你可以輕鬆生成影片,並控制影片呈現方式。文章還提供了在Discord生成影片的指令,文字生成圖片和圖片生成影片的步驟。最後,文章總結了在Discord與官網生成影片的差異。