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() เมื่อไหร่ภาพทั้งหมดจะทะลักออกมา
อ้างอิง