ต่อจาก
บทที่ ๕
ในบทนี้จะพูดถึงเรื่องการย่อหรือขยายขนาดภาพโดยใช้ cv2.resize() และการต่อเติมภาพจากขอบด้วย cv2.copyMakeBorder()
รวมถึงการพลิกภาพกลับโดย cv2.flip()
การย่อขยายขนาดภาพ
ฟังก์ชัน cv2.resize() ใช้สำหรับย่อหรือขยายขนาดภาพให้เป็นขนาดตามที่ต้องการ
สิ่งที่ต้องใส่ในฟังก์ชันคืิอตัวอาเรย์รูปภาพกับขนาดใหม่ที่ต้องการ
ลำดับ |
ชื่อ |
สิ่งที่ต้องใส่ |
ชนิดข้อมูล |
1 |
src |
อาเรย์รูปภาพ |
np.array |
2 |
dsize |
ขนาด (กว้าง,สูง) |
tuple ของ int |
เอาภาพมาย่อดูเป็นตัวอย่าง
teto06c01.jpg
import cv2
import numpy as np
import matplotlib.pyplot as plt
teto = cv2.imread('teto06c01.jpg')
teto = cv2.resize(teto,(120,300))
cv2.imwrite('teto06c02.jpg',teto)
teto06c02.jpg
คราวนี้ลองเอาภาพที่ย่อแล้วมาขยายใหม่
teto = cv2.imread('teto06c02.jpg')
teto = cv2.resize(teto,(600,450))
cv2.imwrite('teto06c03.jpg',teto)
teto06c03.jpg
เวลาภาพเล็กที่ขยายเป็นภาพใหญ่จะเกิดการประมาณค่าในช่วง
แต่จากเดิมทีที่มีรายละเอียดน้อยกว่าอยู่พอขยายจึงมักเห็นภาพแตก
เวลาย่อหรือขยาย ภาพมีวิธีการประมาณค่าในช่วงมีอยู่หลายวิธี ผลที่ได้ก็จะต่างกันไปในรายละเอียด
วิธีการประมาณค่าในช่วงเมื่อย่อขยายภาพ
เมื่อใช้ cv2.resize() สามารถกำหนดวิธีได้โดยใส่คีย์เวิร์ด interporate ค่าที่ใส่เป็นแฟล็กของ cv2 ที่ชื่อขึ้นต้นด้วย
cv2.INTER_
ลองแสดงแฟล็กทั้งหมดได้
print('\n'.join(['%s: %d'%(x,getattr(cv2,x)) for x in dir(cv2) if x[:6]=='INTER_']))
ยกตัวอย่าง ลองใช้วิธีการประมาณค่าในช่วง ๕ แบบต่างกันแล้วเทียบกันดูโดยใช้ matplotlib แสดงภาพเรียงเทียบกันดู
ตัวอย่างแรก ลองเอาภาพเดิมมาย่อ
teto = cv2.imread('teto06c01.jpg')
flag = [None,cv2.INTER_NEAREST,cv2.INTER_LINEAR,cv2.INTER_AREA,cv2.INTER_CUBIC,cv2.INTER_LANCZOS4]
interp = ['','nearest','linear','area','cubic','lanczos4']
plt.figure(figsize=[7,8])
for i in range(6):
plt.subplot(321+i,title=interp[i])
if(flag[i]!=None):
plt.imshow(cv2.resize(teto[:,:,::-1],(64,48),interpolation=flag[i]))
else:
plt.imshow(teto[:,:,::-1])
plt.tight_layout()
plt.show()
จะเห็นว่าผลที่ได้จะแตกต่างกันออกไปเล็กน้อย แต่ก็ไม่ได้เห็นผลชัดเจนนักในกรณีที่ย่อภาพเล็กลง
ต่อมาลองดูตัวอย่างการขยายภาพ
teto06c05.jpg
teto = cv2.imread('teto06c05.jpg')
plt.figure(figsize=[7,10])
for i in range(6):
plt.subplot(321+i,title=interp[i])
if(flag[i]!=None):
plt.imshow(cv2.resize(teto[:,:,::-1],(600,600),interpolation=flag[i]))
else:
plt.imshow(teto[:,:,::-1])
plt.tight_layout()
plt.show()
ผลที่ได้เมื่อขยายภาพจะต่างกันออกไปมาก
การย่อขยายภาพโดยกำหนดค่าเป็นจำนวนเท่าของขนาดเดิม
ฟังก์ชัน cv2.resize() นั้นนอกจากจะเปลี่ยนขนาดโดยกำหนดขนาดใหม่แล้ว
ก็สามารถกำหนดขนาดโดยใช้เลขจำนวนเท่าของขนาดเดิมได้
วิธีการคือให้ใส่อาร์กิวเมนต์ตัวที่ ๒ และ ๓ เป็น None ไว้ แล้วไปใส่อาร์กิวเมนต์ตัวที่ ๔ เป็นเลขจำนวนเท่าของความกว้าง
และตัวที่ ๕ เป็นเลขจำนวนเท่าของความสูง
ตัวอย่าง
gumi06c01.jpg
gumi = cv2.imread('gumi06c01.jpg')
gumi = cv2.resize(gumi,None,None,0.4,0.4)
cv2.imwrite('gumi06c02.jpg',gumi)
gumi06c02.jpg
การพลิกภาพกลับ
cv2.flip() ใช้พลิกภาพกลับตามแนวตั้งหรือนอน
รูปแบบการพลิกมี ๓ แบบ แสดงด้วยตัวเลข -1,0,1
1 |
พลิกแนวนอน |
0 |
พลิกแนวตั้ง |
-1 |
พลิกทั้งแนวตั้งแนวนอน |
ตัวอย่าง
gumi = cv2.imread('gumi06c01.jpg')
plt.figure(figsize=[6,5])
plt.subplot(221)
plt.imshow(gumi[:,:,::-1])
plt.title('ภาพเดิม',family='Tahoma')
for i in [-1,0,1]:
plt.subplot(223+i,title='flip %d'%i)
plt.imshow(cv2.flip(gumi[:,:,::-1],i))
plt.tight_layout()
plt.show()
นอกจากนี้ ถึงไม่ใช่ cv2.flip() แต่ใช้คุณสมบัติของอาเรย์ numpy เองก็สามารถพลิกภาพได้ง่ายๆเช่นกัน
หลักการเช่นเดียวกับที่ใช้ [:,:,::-1] เพื่อกลับสี BGR เป็น RGB เช่น arr[::-1] จะพลิกแนวตั้ง arr[:,::-1]
จะพลิกแนวนอน
การเติมขอบ
cv2.copyMakeBorder() เป็นอีกวิธีหนึ่งในการเพิ่มขนาดรูปภาพ ไม่ใช่ด้วยการขยายซูม
แต่เป็นการเติมขอบโดยเพิ่มเติมจากส่วนที่มีอยู่เดิม
อาร์กิวเมนต์ของฟังก์ชันนี้เป็นดังนี้ ตามลำดับ
ลำดับ |
สิ่งที่ต้องใส่ |
ชนิดข้อมูล |
1 |
อาเรย์ของรูปภาพ |
np.array |
2 |
ส่วนขยายด้านบน |
int |
3 |
ส่วนขยายด้านล่าง |
int |
4 |
ส่วนขยายด้านซ้าย |
int |
5 |
ส่วนขยายด้านขวา |
int |
6 |
วิธีการเติมขอบ |
flag |
ตรงส่วนของวิธีการเติมขอบนั้นใส่เป็นแฟล็ก ซึ่งขึ้นต้นด้วย cv2.BORDER_ ลองไล่ดูว่ามีอะไรบ้างได้
print('\n'.join(['%s: %d'%(x,getattr(cv2,x)) for x in dir(cv2) if x[:7]=='BORDER_']))
ตัวอย่างเพื่อเปรียบเทียบวิธีการเติมขอบในแบบต่างๆ
miku06c01.jpg
miku = cv2.imread('miku06c01.jpg')
plt.figure(figsize=[6,7])
plt.subplot(321)
plt.title('ภาพเดิม',family='Tahoma')
plt.imshow(miku[:,:,::-1])
border = [cv2.BORDER_REPLICATE,cv2.BORDER_REFLECT,cv2.BORDER_REFLECT_101,cv2.BORDER_WRAP,cv2.BORDER_CONSTANT]
title = ['replicate','reflect','reflect101','wrap','constant']
for i in range(5):
plt.subplot(322+i,title=title[i])
plt.imshow(cv2.copyMakeBorder(miku[:,:,::-1],250,150,200,350,border[i]))
plt.tight_layout()
plt.show()
แต่ละแบบมีความหมายดังนี้
แฟล็ก |
เลข |
ความหมาย |
cv2.BORDER_CONSTANT |
0 |
เติมด้วยสีเดียวตลอด |
cv2.BORDER_REPLICATE |
1 |
เอาค่าที่ขอบมาป้ายลากยาวต่อไป |
cv2.BORDER_REFLECT |
2 |
สะท้อนภาพเดิมเหมือนเป็นกระจก |
cv2.BORDER_WRAP |
3 |
ภาพเดิมแผ่ซ้ำต่อไปเรื่อยๆ |
cv2.BORDER_REFLECT_101 |
4 |
สะท้อนภาพเดิม แต่จะไม่ซ้ำจุดริมสุดด้วย |
กรณีที่เลือก cv2.BORDER_CONSTANT ต้องใส่ค่าสีไปด้วยโดยใส่ในคีย์เวิร์ด value ไม่เช่นนั้นจะเป็นสีดำ
ลองใส่สีขอบเป็นสีเขียว
miku = cv2.imread('miku06c01.jpg')
miku = cv2.copyMakeBorder(miku,50,50,50,50,cv2.BORDER_CONSTANT,value=(0,200,50))
plt.imshow(miku[:,:,::-1])
plt.show()
ถ้าใช้ cv2.BORDER_REFLECT ก็สามารถทำภาพเดิมถูกวนซ้ำกลับด้านไปมาเรื่อยๆได้
miku = cv2.imread('miku06c01.jpg')
miku = cv2.copyMakeBorder(miku,0,1800,0,1800,cv2.BORDER_REFLECT)
plt.figure(figsize=[6,6])
plt.imshow(miku[:,:,::-1])
plt.tight_layout()
plt.show()
กรณี cv2.BORDER_WRAP คือแค่วนซ้ำไปเรื่อยๆอาจใช้ np.tile() แทนได้เช่นกัน โดยเขียนแบบนี้
miku = cv2.imread('miku06c01.jpg')
miku = np.tile(miku,(5,4,1))
plt.figure(figsize=[6,6])
plt.imshow(miku[:,:,::-1])
plt.tight_layout()
plt.show()
การทำโมเสกหรือเซ็นเซอร์ภาพ
หากย่อภาพให้เหลือความละเอียดต่ำแล้วขยายภาพกลับขึ้นมาใหม่โดยใช้วิธีการ cv2.INTER_NEAREST
ก็จะทำให้ได้ภาพที่เป็นแผ่นสี่เหลี่ยมใหญ่ๆเหมือนแผ่นกระเบื้องโมเสก ดูสวยไปอีกแบบ
ที่เป็นอย่างนี้ได้เพราะวิธีการนี้จะขยายโดยดูว่าจุดใกล้สุดเป็นสีอะไร พิจารณาอยู่แค่ช่องเดียว
เมื่อขยายจึงมีหลายจุดเป็นสีเดียวกันต่อเนื่องเป็นสี่เหลี่ยม
ตัวอย่างการใช้
rin06c01.jpg
rin = cv2.imread('rin06c01.jpg')
rinx = cv2.resize(rin,(120,90))
rinx = cv2.resize(rinx,(600,450),interpolation=cv2.INTER_NEAREST)
cv2.imwrite('rin06c02.jpg',rinx)
rinx = cv2.resize(rin,(60,45))
rinx = cv2.resize(rinx,(600,450),interpolation=cv2.INTER_NEAREST)
cv2.imwrite('rin06c03.jpg',rinx)
rin06c02.jpg
rin06c03.jpg
ก็จะได้ภาพออกมาเป็นสี่เหลี่ยมช่องๆเหมือนถูกเซนเซอร์แบบนี้
สามารถเลือกเซ็นเซอร์บางส่วนได้ โดยเลือกเฉพาะแต่ส่วนที่ต้องการแก้ เช่นลองเลือกเฉพาะบริเวณแก้วกาแฟ
rin = cv2.imread('rin06c01.jpg')
rinx = cv2.resize(rin[320:440,300:500],(15,9))
rin[320:440,300:500] = cv2.resize(rinx,(200,120),interpolation=0)
cv2.imwrite('rin06c04.jpg',rin)
rin06c04.jpg
ใครต้องการเซ็นเซอร์เหล้าหรือเบียร์ก็สามารถใช้วิธีการแบบนี้ได้
อ่านบทถัดไป >>
บทที่ ๗