φυβλαςのβλογ
phyblasのブログ



numpy & matplotlib เบื้องต้น บทที่ ๔๐: การจัดการรูปภาพ
เขียนเมื่อ 2016/07/04 00:53
แก้ไขล่าสุด 2021/09/28 16:42
matplotlib และ matplotlib นั้นนอกจากจะใช้วาดกราฟและแผนภาพแล้วก็ยังใช้ในการจัดการภาพทั่วๆไปได้ด้วย

เรื่องของการจัดการรูปภาพนั้นมีรายละเอียดมากมาย สามารถใช้ทำอะไรได้มากมาย แต่ในที่นี้จะพูดถึงแค่เบื้องต้นเท่านั้น



การใช้ imshow เพื่อแสดงภาพจากค่าสี
ฟังก์ชันที่ใช้แสดงผลภาพใน matplotlib คือ plt.imshow ฟังก์ชันนี้ดูเผินๆจะคล้าย plt.pcolor และ plt.pcolormesh ซึ่งเคยกล่าวถึงไปแล้วในบทที่ ๒๔

นั่นคือทั้ง pcolor, pcolormesh และ imshow จะนำอาเรย์สองมิติมาแสดงผลเป็นสีๆ แต่มีข้อแตกต่างกันอยู่หลายอย่างซึ่งสามารถเห็นได้ชัด

ขอเริ่มยกตัวอย่างการใช้โดยเปรียบเทียบ imshow กับ pcolor โดยสร้างอาเรย์ที่มีค่าตัวเลขไล่เรียงกัน ๙ ตัวแล้วนำมาขึ้นรูปใหม่เป็นอาเรย์ 3x3 จากนั้นนำมาวาดแผนภาพไล่สีด้วย pcolor พร้อมกับ imshow
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(9).reshape(3,3)
plt.figure(figsize=[7,7])
plt.subplot(211,title='imshow')
plt.imshow(x,cmap='gray')
plt.subplot(212,title='pcolor')
plt.pcolor(x,cmap='gray')
plt.show()



จากตัวอย่างนี้จะทำให้เห็นข้อแตกต่างได้หลายข้อ เช่น
- imshow จะแสดงแกนตั้งโดยเริ่มจาก 0 ที่ด้านบนสุด แต่ pcolor จะเริ่มจากด้านล่างสุด
- เมื่อใช้ imshow สัดส่วนระหว่างแกน x และ y จะถูกตั้งให้เท่ากันเสมอ แต่ pcolor จะถูกปรับตามความเหมาะสม
- แต่ละช่องใน imshow จะมีใจกลางอยู่ที่ตำแหน่งตามค่า แต่ pcolor จะวางอยู่ระหว่างค่านั้นกับค่าถัดไป

นอกจากนี้ยังมีข้อแตกต่างอื่นๆที่จะเห็นได้ต่อไปอีก เพราะ imshow มีคีย์เวิร์ดที่ใส่เพิ่มเติมลงไปเพื่อปรับแต่งอะไรได้มากกว่าที่ pcolor มี

หากจะปรับแต่ง imshow ให้ดูคล้ายกับ pcolor ก็อาจทำได้โดยตั้งคีย์เวิร์ด aspect และ origin

aspect คือสัดส่วนขนาด y ต่อแกน x ซึ่งโดยทั่วไป imshow จะทำการปรับสัดส่วนแกนกราฟให้เป็น 1 คือ x y เท่ากัน เพื่อให้เป็นภาพตามสัดส่วนจริง แต่หากต้องการให้ปรับแผ่กว้างตามพื้นที่ที่มีโดยอัตโนมัติ auto

ส่วน origin จะกำหนดว่าจะเริ่มตำแหน่ง 0 ที่บนหรือล่าง ถ้า upper คือบน ถ้า lower คือล่าง โดยทั่วไปจะถูกตั้งเป็น upper แต่เราสามารถตั้งเป็น lower ได้

ลองปรับตามนี้จะได้ผลคล้ายกับ pcolor คือเริ่ม 0 จากด้านล่าง แล้วก็ไม่มีการปรับสัดส่วนของภาพแนวตั้งและนอนให้เท่ากัน
x = np.arange(81).reshape(9,9)
plt.imshow(x,cmap='bone',aspect='auto',origin='lower')
plt.show()



แต่ก็จะยังต่างกันอยู่เพราะตำแหน่งของช่องแต่ละช่องจะวางโดยมีใจกลางอยู่ที่ค่าตัวเลข ในขณะที่ pcolor จะวางช่องไว้ระหว่างค่า

นอกจากนี้ imshow ยังทำให้มีการประมาณค่าระหว่างช่วงได้ แทนที่จะแสดงเป็นช่องๆ รูปแบบการประมาณค่าในช่วงกำหนดโดยคีย์เวิร์ด interpolation สามารถเลือกได้หลากหลายรูปแบบมาก เช่นหากตั้งเป็น none ก็จะไม่มีการประมาณค่าในช่วงและจะออกมาเหมือน pcolor

ตัวอย่าง
x = np.sin(np.arange(400).reshape(20,20)/2.)
plt.figure(figsize=[6,12])
plt.subplot(211)
plt.imshow(x,cmap='rainbow',interpolation='none')
plt.subplot(212)
plt.imshow(x,cmap='rainbow',interpolation='hanning')
plt.show()

ภาพด้านบนไม่มีการประมาณค่าในช่วง ส่วนภาพด้านล่างมีการประมาณค่าในช่วงแบบ hanning



สำหรับแบบอื่นๆดูตัวอย่างได้ใน http://matplotlib.org/examples/images_contours_and_fields/interpolation_methods.html

และ imshow ไม่สามารถจะใส่ค่าพิกัด x,y ของแต่ละจุดได้ แต่หากต้องการปรับตำแหน่งหรือขนาดของภาพจะใช้คีย์เวิร์ด extent แทน

รูปแบบการใส่ค่าคือ extent=(ซ้าย,ขวา,บน,ล่าง)

เช่นปรับตามนี้จะได้ภาพอยู่ในตำแหน่งแนวนอนจาก 20 ถึง 35, แนวตั้งจาก 5 ถึง 15
x = np.cos(np.arange(600).reshape(20,30)/2.)
plt.imshow(x,cmap='jet',extent=(20,35,5,15),interpolation='spline16')
plt.show()



จะเห็นว่าเราสามารถปรับภาพให้ยืดขยายตามที่ต้องการได้ง่ายดาย เพียงแต่ว่าตำแหน่งแต่ละจุดต้องเรียงห่างเท่ากันเสมอ จะกำหนดจุดให้อยู่ตรงไหนห่างกันเท่าไหร่ก็ได้เหมือนอย่าง pcolor ไม่ได้



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

ค่าสีอาจใส่ในรูปแบบของจำนวนจริงตั้งแต่ 0 ถึง 1 หรืออาจแสดงเป็นตัวเลข 0 ถึง 255 ก็ได้

ในกรณีที่จะใช้ค่าสี 0 ถึง 255 จะต้องใช้อาเรย์ที่มีชนิดข้อมูลเป็น uint8 ซึ่งอาจทำได้โดยการใช้เมธอดแปลง astype('uint8')

ตัวอย่าง ตั้งค่าสีให้มีส่วนของสีเขียวเพิ่มขึ้นเรื่อยๆตามแนวนอน และสีน้ำเงินเพิ่มตามแนวตั้ง ส่วนสีแดงเป็น 0 ทั้งหมด
a,b = np.meshgrid(np.arange(0,125),np.arange(0,100))
x1 = np.zeros([100,125])
x2 = a/125.
x3 = b/100.
x = np.stack((x1,x2,x3),axis=2)
plt.imshow(x)
plt.show()



คราวนี้ลองเขียนให้อยู่ในรูปของค่า 0 ถึง 255 เปลี่ยนให้สีแดงเพิ่มตามแนวนอน สีเขียวเพิ่มตามแนวตั้ง ส่วนสีน้ำเงินคงที่
a,b = np.meshgrid(np.arange(0,125),np.arange(0,100))
x1 = a*255/125
x2 = b*255/100
x3 = np.zeros([100,125])+127
x = np.stack((x1,x2,x3),axis=2).astype('uint8')
plt.imshow(x)
plt.show()



ทีนี้ก็จะเห็นแล้วว่า imshow สามารถนำค่าแม่สีทั้งสามมาแสดงผลได้ทำให้ได้ภาพที่มีสีสันตามที่ต้องการ

ถ้าหากข้อมูลในอาเรย์นั้นคือค่าสีของจุดแต่ละพิกเซลของรูปภาพ เราก็สามารถแสดงผลรูปภาพลงภายในนี้ได้



การเปิดภาพขึ้นมาจากไฟล์
เราสามารถโหลดข้อมูลสีจากไฟล์ภาพทั่วไปขึ้นมาเพื่อใส่ในอาเรย์ได้โดยฟังก์ชัน imread ในมอดูลย่อย image ของ matplotlib มอดูลนี้มีไว้สำหรับจัดการภาพโดยเฉพาะ

โดยทั่วไป matplotlib.image นั้นตอน import มักนิยมย่อเป็น mpimg ในที่นี้ก็จะใช้ตามนั้น
import matplotlib.image as mpimg

ในส่วนนี้ขอยกภาพจากอนิเมะเรื่อง gochuumon wa usagi desu ka? มาใช้เป็นตัวอย่าง

ที่มาของภาพทั้งหมดมาจาก
ภาพที่โหลดมาจะอยู่ในรูปอาเรย์สามมิติซึ่งแทนตำแหน่งในแนวตั้ง, ตำแหน่งในแนวนอน และค่าของแม่สีทั้งสาม

ค่าภายในนั้นอาจมีทั้งที่เป็นชนิด uint8 ที่มีค่าตั้งแต่ 0 ถึง 255 หรือชนิด float32 ที่มีค่าตั้งแต่ 0 ถึง 1 แล้วแต่ชนิดไฟล์ จะแบบไหนก็สามารถแสดงสีได้ไม่ต่างกัน

ลองเปิดดูเป็นตัวอย่าง
import matplotlib.image as mpimg
chino = mpimg.imread('chino01.jpg')
print(chino)
print(chino.shape)



ได้
[[[246 237 228]
  [246 237 228]
  [246 237 228]
  ...,

  ...,
  [245 236 229]
  [255 249 241]
  [250 243 235]]]
(700, 501, 3)

สามารถนำมาใช้ใน imgshow เพื่อแสดงภาพได้ทันที
plt.figure(figsize=[6,7])
plt.imshow(chino)
plt.show()





เราสามารถลองเอาภาพมาวิเคราะห์เพื่อแยกดูส่วนประกอบในแต่ละสีได้
chino = mpimg.imread('chino02.jpg')
plt.subplot(221)
plt.imshow(chino)
plt.subplot(222)
plt.imshow(chino*np.array([1,0,0],dtype='uint8'))
plt.subplot(223)
plt.imshow(chino*np.array([0,1,0],dtype='uint8'))
plt.subplot(224)
plt.imshow(chino*np.array([0,0,1],dtype='uint8'))
plt.subplots_adjust(0.05,0.05,0.98,0.98,0.1,0.1)
plt.show()






การปรับแต่งภาพ
เมื่อเราได้เห็นแล้วว่าภาพสวยๆที่เห็นนั้นตัวจริงประกอบไปด้วยตัวเลขต่างๆในแถ วอาเรย์ หากเปลี่ยนแปลงแก้ไขตัวเลขภายในอาเรย์ก็จะสามารถทำให้ภาพเปลี่ยนแปลงไปได้ ในที่นี้เราจะมาลองจัดการภาพอย่างง่ายๆกัน

เริ่มแรกลองมาทำภาพโทนขาวดำ ซึ่งสามารถทำได้ง่ายๆโดยนำสามสีมาเฉลี่ยกัน
chino = mpimg.imread('chino03.jpg')
chino = (chino.sum(axis=2)[:,:,None]/3*np.array([1,1,1])).astype('uint8')
plt.figure(figsize=[6,7])
plt.axes([0.05,0.05,0.93,0.93])
plt.imshow(chino)
plt.show()




หรืออาจลองเปลี่ยนสีให้เป็นสีตรงข้ามโดยเอา 255 มาลบ
chino = mpimg.imread('chino04.jpg')
chino = 255-chino
plt.axes([0.05,0.01,0.92,0.99])
plt.imshow(chino)
plt.show()




ทำให้ภาพเข้มขึ้นหรืออ่อนลงได้
chino = mpimg.imread('chino05.jpg')
plt.figure(figsize=[9,6])
plt.subplot(121)
plt.imshow((chino*0.5).astype('uint8'))
plt.subplot(122)
plt.imshow((128+chino*0.5).astype('uint8'))
plt.subplots_adjust(0.05,0.05,0.98,0.98,0.1,0.1)
plt.show()




ตัดภาพมาบางส่วน
chino = mpimg.imread('chino06.jpg')
plt.subplot(121)
plt.imshow(chino[30:380,100:280])
plt.subplot(122)
plt.imshow(chino[0:330,290:480])
plt.subplots_adjust(0.05,0.05,0.98,0.98,0.1,0.1)
plt.show()




เราอาจเข้าถึงแล้วเปลี่ยนข้อมูลบางส่วนให้เป็นสีขาว สร้างภาพที่ขาดไปเป็นริ้วๆได้
chino = mpimg.imread('chino07.jpg')+0
chino[:,::6] = 255
plt.figure(figsize=[6,7])
plt.axes([0,0.05,1,0.9])
plt.imshow(chino)
plt.show()




ลองทำให้สว่างมืดสลับกันไปเป็นลูกคลื่น
chino = mpimg.imread('chino08.jpg')
x = np.arange(chino.shape[1])
chino = ((0.7+0.3*np.sin(x/20.))[:,None]*chino).astype('uint8')
plt.figure(figsize=[6,7])
plt.axes([0,0.05,1,0.9])
plt.imshow(chino)
plt.show()




ใช้ tile เพื่อเปลี่ยนอาเรย์ให้เป็นภาพเรียงซ้ำกัน
chino = mpimg.imread('chino09.jpg')
plt.figure(figsize=[7,7])
plt.axes([0.05,0.1,0.94,0.8])
plt.imshow(np.tile(chino,[3,4,1]))
plt.show()




สำหรับคำสั่งต่างๆที่ใช้จัดการภาพโดยเฉพาะซึ่งจะทำให้ปรับแต่งอะไรได้อย่างสะดวก หลากหลายนั้นอาจลองใช้มอดูลอื่นๆเสริมเพิ่มเติม เช่น PIL, skimage, opencv, ฯลฯ



การบันทึกภาพ
สามารถบันทึกภาพได้โดยใช้ฟังก์ชัน imsave

ตัวอย่าง
x,y = np.meshgrid(np.arange(300),np.arange(300))
z1 = np.sin(((x-150)**2+(y-150)**2)/1100.)**2
z2 = 0.6+np.cos(np.sqrt((x-150)**2+(y*2-150)**2)/4)*0.4
z3 = np.random.rand(300,300)
z = np.stack([z1,z2,z3],axis=2)
mpimg.imsave('c40a15.jpg',z)



เท่านี้ก็จะได้ภาพออกมาตามที่ต้องการ



การนำภาพมาใส่เป็นฉากหลัง
บางคนทำงานอยู่กับกราฟตลอด อาจรู้สึกเบื่อกับฉากหลังขาวๆธรรมดา ต้องการใส่รูปสวยๆเพื่อให้ดูมีสีสันขึ้นมา เราสามารถใช้ imshow เพื่อเพิ่มรูปลงไปได้

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

หนทางอาจมีหลายวิธี เช่นรับค่าขอบเขตของกราฟเดิมมาด้วยเมธอด ใส่คีย์เวิร์ด get_xlim และ get_ylim จากนั้นก็ปรับขอบเขตให้เป็นไปตามค่าที่ได้โดยใส่ในคีย์เวิร์ด extent แล้วตั้งค่า zorder ให้น้อยติดลบไว้เพื่อให้อยู่ด้านหลังเสมอ

อาจลองทำเป็นฟังก์ชันขึ้นมาเพื่อนำมาใส่ตอนท้ายได้

ลองทำดูเป็นตัวอย่าง
def chinobg(ax):
    chino = mpimg.imread('chino10.jpg')
    ext = ax.get_xlim()+ax.get_ylim()
    ax.imshow(chino,extent=ext,zorder=-1,interpolation='gaussian')

x = np.linspace(0,1000,101)
plt.figure(figsize=[6,7])
ax = plt.axes([0.05,0.05,0.93,0.92])
for i in range(30):
    y = np.random.normal(10*np.random.rand(),10+5*np.random.rand(),101).cumsum()
    pl = plt.plot(x,y,c=np.random.rand(3))
chinobg(ax)
plt.show()




เท่านี้ก็ได้กราฟที่มีฉากหลังสวยๆแล้ว



อ้างอิง


<< บทที่แล้ว     บทถัดไป >>
หน้าสารบัญ


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

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

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

หมวดหมู่

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

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

目次

日本による名言集
モジュール
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
機械学習
-- ニューラル
     ネットワーク
javascript
モンゴル語
言語学
maya
確率論
日本での日記
中国での日記
-- 北京での日記
-- 香港での日記
-- 澳門での日記
台灣での日記
北欧での日記
他の国での日記
qiita
その他の記事

記事の類別



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

  記事を検索

  おすすめの記事

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

ไทย

日本語

中文