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



[python] สร้างหรือจัดการภาพ .gif ด้วย imageio
เขียนเมื่อ 2018/06/03 11:25
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.gca(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

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
python
-- numpy
-- matplotlib

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

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

ไทย

日本語

中文