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



opencv-python เบื้องต้น บทที่ ๖: การปรับขนาดและต่อเติมภาพ
เขียนเมื่อ 2020/06/28 18:47
แก้ไขล่าสุด 2024/02/22 10:26

ต่อจาก บทที่ ๕

ในบทนี้จะพูดถึงเรื่องการย่อหรือขยายขนาดภาพโดยใช้ 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

ใครต้องการเซ็นเซอร์เหล้าหรือเบียร์ก็สามารถใช้วิธีการแบบนี้ได้



อ่านบทถัดไป >> บทที่ ๗



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

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

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

หมวดหมู่

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

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

目次

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

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

記事の類別



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

  記事を検索

  おすすめの記事

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

ไทย

日本語

中文