φυβλαςのβλογ
บล็อกของ phyblas



[python] สร้างหรือจัดการภาพ .gif ด้วย imageio
เขียนเมื่อ 2018/06/03 11:25
แก้ไขล่าสุด 2024/02/22 09:46
imageio เป็นมอดูลหนึ่งของไพธอนที่ใช้จัดการกับไฟล์รูปภาพ และยังทำอะไรกับไฟล์วีดีโอได้ด้วย

หนึ่งในความสามารถที่โดดเด่นที่มักนิยมใช้ก็คือ ใช้เพื่อสร้างหรืออ่านภาพ gif ดังนั้นในบทความนี้จะเขียนถึงวิธีการสร้างและจัดการภาพ .gif ด้วย imageio



การติดตั้ง

สามารถใช้ pip ลงได้ทันที
pip install imageio


เปิดอ่านไฟล์ gif ขึ้นมา

imageio มีคำสั่ง imread() และ mimread() สำหรับอ่านไฟล์ โดย imread() ใช้สำหรับอ่านภาพธรรมดาภาพเดียว ต่อให้ใช้คำสั่งนี้ gif ก็โผล่มาแค่ภาพเดียว แต่หากต้องการให้อ่านภาพทั้งหมดในไฟล์ gif จะใช้คำสั่ง mimread()

ขอใช้ภาพนี้เป็นตัวอย่าง กดโหลดไปลองใช้ทำตามได้



เปิดอ่านด้วยโค้ดตามนี้
import imageio as imo
ruamrupphap = imo.mimread('dcgan.gif')
print(len(ruamrupphap)) # 32
print(type(ruamrupphap[0])) # <class 'imageio.core.util.Image'>
print(ruamrupphap[0].shape) # (80, 80, 4)
print(ruamrupphap[0].meta) # ได้ Dict([('version', b'GIF89a'), ('background', 0), ('extension', (b'NETSCAPE2.0', 795)), ('loop', 65535), ('duration', 200)])

เมื่อเปิดอ่าน สิ่งที่ได้คือลิสต์ของภาพแต่ละเฟรม โดยแต่ละภาพจะอยู่ในรูปของอาเรย์สามมิติของค่าความเข้มแต่ละสี ขนาดเป็น (สูง,กว้าง,4) โดยแสดงค่า (แดง,เขียว,น้ำเงิน,อัลฟา) ในแต่ละจุด ค่าเป็นจำนวนเต็มชนิด uint8 ค่าอยู่ในช่วง 0-255

แต่ถ้าเป็นภาพขาวดำจะเป็นอาเรย์สองมิติขนาด (สูง,กว้าง) โดยแสดงแค่ค่าความเข้มในแต่ละจุด

ชนิดของอาเรย์ที่ได้นี่จริงๆแล้วไม่ใช่อาเรย์ธรรมดา แต่เป็นคลาส imageio.core.util.Image ซึ่งโดยพื้นฐานแล้วก็คืออาเรย์ แต่มีเพิ่มเติมคุณสมบัติบางอย่างไป เช่นมีแอตทริบิวต์ .meta ซึ่งเก็บคุณสมบัติบางอย่างของภาพไว้อยู่ เช่น สำหรับภาพ gif มี duration คือระยะเวลา เป็นหน่วยมิลลิวินาที

ลองเอาภาพมาเปิดดูใน matplotlib ได้
import matplotlib.pyplot as plt
plt.imshow(ruamrupphap[0])
plt.show()


สามารถนำภาพที่อ่านออกมาได้มาเขียนแยกเป็นไฟล์ภาพเดี่ยวๆได้ โดยใช้ imsave() หรือ imwrite() ก็ได้ ที่จริงเป็นคำสั่งเดียวกัน แต่ถูกสร้างไว้ให้ใช้ได้สองชื่อ

ถ้าจะบันทึกเป็น png ก็ทำได้เลย
for i in range(len(ruamrupphap)):
    imo.imwrite('dcgan%d.png'%(i+1),ruamrupphap[i])
กรณีจะบันทึกเป็น jpg ต้องทำเป็นอาเรย์ขนาด (สูง,กว้าง,3) คือเอาค่าอัลฟาออกก่อน
for i in range(len(ruamrupphap)):
    imo.imwrite('dcgan%d.jpg'%(i+1),ruamrupphap[i][:,:,:3])
ก็จะได้ภาพแยกออกมา





การสร้างภาพ gif ใหม่

คำสั่ง imwrite() มีไว้ใช้บันทึกได้แค่รูปภาพเดี่ยว แต่หากต้องการเขียนไฟล์ gif ภาพเคลื่อนไหวสามารถใช้คำสั่ง mimsave() หรือ mimwrite()

ภาพที่อ่านขึ้นมาจากตัวอย่างที่แล้วเราอาจนำมาใช้บันทึกกลับเป็นไฟล์ gif อีกไฟล์ได้ทันที โดยเวลาเขียนต้องใส่คีย์เวิร์ดอัตราต่อวินาที (fps) หรือระยะห่างแต่ละเฟรม (duration) หน่วยเป็นวินาที เช่น
imo.mimwrite('rupphapmai.gif',ruamrupphap,duration=0.1)
หรือ
imo.mimwrite('rupphapmai.gif',ruamrupphap,fps=10)
เท่านี้ก็จะได้ภาพใหม่ที่มีเปลี่ยนภาพวินาทีละ ๑๐ ภาพ



นอกจากนี้ยังมีอีกวิธี คือใช้ get_writer แบบนี้ ซึ่งจะดูยุ่งยากกว่าหน่อย โดยทั่วไปใช้ mimwrite จะง่ายกว่า
with imo.get_writer('rupphapmai.gif',mode='I',fps=10) as f:
    for rupphap in ruamrupphap:
        f.append_data(rupphap)



สร้างภาพ gif จากอาเรย์

อาจลองสร้างอาเรย์ค่าสีขึ้นมาแล้วเขียนใหม่เป็นไฟล์ก็ได้ เช่น
import numpy as np
x,y = np.meshgrid(np.linspace(-7.5,7.5,300),np.linspace(-5,5,200))
ruamrupphap = []
for i in range(10):
    z = (7.5-np.abs(x))/7.5*np.sin((np.sqrt(x**2+y**2)/2+i/10)*np.pi)**2
    z = np.stack([z,np.random.uniform(0,1,z.shape),1-z],2)
    z = (z*255).astype(np.uint8)
    ruamrupphap.append(z)
imo.mimwrite('khluen.gif',ruamrupphap,fps=10)

(คำเตือน: อย่าจ้องภาพนี้นานมากจะดีกว่า)

อาเรย์ที่ใช้เป็นภาพอาจใช้เป็นอาเรย์ขนาด (สูง,กว้าง,3) อย่างนี้ก็ได้ โดยมีแค่ค่า (แดง,เขียว,น้ำเงิน) ไม่ต้องมีค่าที่ ๔ คือค่าอัลฟา หรือจะใส่ค่าอัลฟาไปด้วยก็ได้เช่นกัน หรือถ้าจะเป็นภาพขาวดำก็แค่อาเรย์สองมิติ (สูง,กว้าง)

ตรง z = (z*255).astype(np.uint8) นี่คือเพื่อเปลี่ยนให้เป็นอาเรย์ของค่าจำนวนเต็มที่มีค่า 0-255 แต่ถึงไม่แปลงเองถ้าเราใช้อาเรย์ที่มีค่า 0-1 มันก็จะแปลงให้อัตโนมัติ แต่จะมีข้อความเตือนขึ้นว่ามีการแปลงให้ ซึ่งบางทีอาจไม่ได้ผลอย่างที่ต้องการ ดังนั้นแปลงเองอาจจะดีกว่า



สร้างภาพ gif จากกลุ่มไฟล์รูปภาพที่มีอยู่

ถ้ามีกลุ่มรูปภาพแต่ละเฟรมอยู่แล้วอยากนำมาสร้างเป็นภาพเคลื่อนไหวก็สามารถใช้คำสั่ง imread เพื่อเปิดไฟล์รูปภาพทั้งหมดที่ต้องการ แล้วก็นำรูปมารวมไว้ในลิสต์ แล้วก็เขียนออกมาเป็นไฟล์ gif

เช่นลองใช้ภาพเหล่านี้เป็นตัวอย่าง



ภาพเอามาจากที่นี่ https://phyblas.hinaboshi.com/20100517

อาจใส่ไว้ในโฟลเดอร์เดียวแล้วใช้คำสั่งเช่น glob เพื่อรวบรวมชื่อไฟล์ภาพทั้งหมด
from glob import glob
chuefilerupphap = glob('meme*.jpg')
ruamrupphap = [imo.imread(r) for r in chuefilerupphap]
imo.mimwrite('meme.gif',ruamrupphap,fps=10)

ก็จะได้ภาพนี้





สร้างภาพ gif จาก matplotlib

มีหลายวิธีในการสร้างภาพเคลื่อนไหวจาก matplotlib เช่นใช้ ImagMagick แต่ว่าในที่นี้จะใช้ imageio ทำ ซึ่งดูแล้วเข้าใจง่ายกว่าหน่อย

ปกติภาพที่วาดภายใน matplotlib นั้นสามารถวาดออกมาเป็นไฟล์ได้ด้วยคำสั่ง plt.savefig() เราสามารถสร้างภาพแต่ละเฟรมแล้วเขียนลงไฟล์ภาพเป็น png ไว้ แล้วค่อยนำมารวมเป็น gif ทีหลังได้

แต่ว่าทำแบบนั้นคือต้องเขียนไฟล์แล้วก็อ่านแล้วลบอีกที ดังนั้นจึงดูยุ่งยากและไม่สะดวก

วิธีที่ดีกว่าคือเปลี่ยนภาพใน matplotlib มาเป็นอาเรย์โดยตรง ทำได้โดยใช้ fig.canvas.draw แล้วตามด้วย fig.canvas.renderer._renderer

ตัวอย่างการใช้เป็นไปตามนี้ เช่น ลองวาดภาพแสดงการเกิดคลื่นนิ่งจากคลื่นสองขบวนที่มาชนกัน
x = np.linspace(0,1,500)
ruamrupphap = []
for i in range(30):
    fig = plt.figure(figsize=[3,2])
    plt.axes(ylim=[-2,2])
    y1 = np.where(x<i/20,np.sin((i/20-x)*2*np.pi*2),0)
    y2 = np.where(1-x<i/20,np.sin((i/20+x)*2*np.pi*2),0)
    y = y1+y2
    plt.plot(x,y)
    plt.tight_layout()
    fig.canvas.draw()
    ruamrupphap.append(np.array(fig.canvas.renderer._renderer))
    plt.close()
imo.mimwrite('khluenning.gif',ruamrupphap,fps=10)


เท่านี้ก็ได้กราฟที่เป็นภาพเคลื่อนไหว แสดงการเกิดคลื่นนิ่ง

ที่ต้องระวังอย่างหนึ่งคือ อย่างลืมสั่ง plt.close() เพื่อปิดภาพในแต่ละครั้งที่วาดด้วย ไม่เช่นนั้นภาพจะหลงเหลือสะสมอยู่แม้จะไม่ได้แสดง แต่เมื่อสั่ง plt.show() เมื่อไหร่ภาพทั้งหมดจะทะลักออกมา



อ้างอิง


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

ไทย

日本語

中文