φυβλαςのβλογ
บล็อกของ 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
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

บทความแต่ละเดือน

2024年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文