φυβλαςのβλογ
phyblas的博客



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

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

ในบทนี้จะพูดถึงการนำรูปภาพ ๒ รูปขึ้นไปมาทำอะไรกัน โดยพื้นฐานแล้วก็คือเป็นการบวกลบคูณหารอาเรย์ numpy โดยมีการใช้ฟังก์ชันของ opencv เข้าร่วมในการคำนวณด้วยซึ่งเหมาะสมสำหรับใช้ในการคำนวณความสว่างของภาพ โดยเฉพาะที่อยู่ในรูปแบบ uint8




การบวกลบรูปภาพ

ข้อมูลรูปภาพเป็นอาเรย์ของ numpy ดังนั้นหากมีภาพที่ขนาดเท่ากัน ๒ ภาพก็สามารถนำมาบวกลบกันได้ด้วยเครื่องหมาย + - เลย อย่างง่าย

แต่ปัญหาก็คือ ข้อมูลรูปภาพซึ่งส่วนใหญ่จะใช้เป็นข้อมูลชนิด uint8 นั้นมีค่าเป็นได้แค่จำนวนเต็มตั้งแต่ 0 ถึง 255 เท่านั้น ถ้าหากเมื่อบวกกันแล้วเกิดค่าต่ำกว่า 0 หรือเกิน 255 ขึ้นมาก็จะทำให้เกิดปัญหาได้

หากเอาอาเรย์มาบวกลบกันธรรมดาแล้วค่าเกิน 255 ค่านั้นก็จะถูกหารปัดเศษ

ตัวอย่างเช่น สร้าง ๒ ภาพขาวดำที่มีการไล่สีขาวดำแนวตั้งและแนวนอน
import cv2
import numpy as np

rup1 = np.tile(np.arange(256),(256,1))
rup2 = rup1.T
cv2.imwrite('imw03c1.png',rup1)
cv2.imwrite('imw03c2.png',rup2)

imw03c01.png

imw03c02.png

ลองเอามาบวกกัน
cv2.imwrite('imw03c3.png',rup1+rup2)

imw03c03.png

ผลที่ได้จะออกมาแปลกๆอย่างที่เห็น นั่นคือส่วนที่ค่าเกิน 255 ไปจะกลับมาเป็น 0 นั่นคือค่าจะกลายเป็นเหมือนถูกหารด้วย 256 แล้วเอาเศษ (x = x%256)

หรือถ้าเอามาลบกัน ส่วนที่ต่ำกว่า 0 ก็จะกลับมาเป็น 255 อีก จึงออกมาแปลกๆแบบนี้
cv2.imwrite('imw03c3.png',rup1+rup2)

imw03c04.png

ซึ่งโดยทั่วไปเราคงจะไม่ได้ต้องการแบบนี้ ดังนั้นการนำอาเรย์รูปภาพมาบวกกันโดยตรงจึงไม่ควรนัก

แทนที่จะทำแบบนั้น เพื่อความปลอดภัยอาจใช้ฟังก์ชัน cv2.add() เวลาบวกกัน และ cv2.subtract() เวลาลบกันแทน ข้อแตกต่างก็คือถ้าใช้ฟังก์ชันนี้บวกหรือลบแล้วค่าเกิน 255 ก็จะกลายเป็น 255 ไม่เกินนั้น ถ้าต่ำกว่า 0 ก็จะเป็น 0
cv2.imwrite('imw03c5.png',cv2.add(rup1,rup2))
cv2.imwrite('imw03c6.png',cv2.subtract(rup1,rup2))

imw03c05.png

imw03c06.png

คราวนี้ส่วนที่เกินจึงกลายเป็นขาวและดำไป

ภาพสีก็นำมาบวกลบได้เช่นเดียวกัน โดยสีเดียวกันก็จะบวกกันไป

ลองสร้างภาพที่มีเสียงไล่เรียงไปในแนวตั้งและแนวนอนคนละสีแล้วเอามาผสมกันดู
rup3 = np.ones([256,256,3])*255
rup3[:,:,0] = 0
rup3[:,:,1] = np.arange(256)
cv2.imwrite('imw03c07.png',rup3) # ไล่ซ้ายขวา แดงไปเหลือง

rup4 = np.zeros([256,256,3])
rup4[:,:,0] = 255
rup4[:,:,1] = np.arange(256).reshape(256,1)
cv2.imwrite('imw03c08.png',rup4) # ไล่บนล่าง น้ำเงินไปฟ้า

cv2.imwrite('imw03c09.png',cv2.add(rup3,rup4))
cv2.imwrite('imw03c10.png',cv2.subtract(rup3,rup4))
cv2.imwrite('imw03c11.png',cv2.subtract(rup4,rup3))

imw03c07.png

imw03c08.png

imw03c09.png

imw03c10.png

imw03c11.png

ผลลัพธ์มีหลายส่วนที่มีสีเป็นค่าเกิน 255 หรือต่ำกว่า 0 ส่วนที่เกินขอบเขตไปก็ถูกตัดเป็น 255 หรือ 0 ไป

ข้อแตกต่างจากการบวกลบอาเรย์ธรรมดาอีกอย่างก็คือ จะต้องทำกับข้อมูลชนิดเดียวกันเท่านั้น ถ้ามี uint8 จะไปบวกลบกับ float32 ก็ต้องแปลงตัวใดตัวหนึ่งเป็นอีกตัวก่อน ต่างจากเมื่อบวกกันธรรมดา

ลองเปรียบเทียบกันดู
arr1 = np.array([1],dtype=np.float32)
arr2 = np.array([1],dtype=np.uint8)
print(arr1+arr2) # ได้ [2.]
print(cv2.add(arr1,arr2)) # ได้ error: (-5:Bad argument) When the input arrays in add/subtract/multiply/divide functions have different types, the output array type must be explicitly specified in function 'arithm_op'

อาเรย์ใน numpy ที่บวกลบกันธรรมดานั้นถ้าจำนวนเต็มบวกกับจำนวนจริงก็จะได้ผลเป็นจำนวนจริง




การหาค่าส่วนต่างระหว่างรูปภาพ

นอกจากบวกลบกันธรรมดาแล้ว บางครั้งอาจต้องการเอา ๒ ภาพมาเทียบแล้วหักลบกันโดยหาส่วนต่าง ซึ่งทำได้โดยฟังก์ชัน cv2.absdiff()

ตัวอย่างเทียบระหว่างหักลบด้วย cv2.absdiff() กับใช้ c2.subtract()

ใช้กับภาพขาวดำ

miku03c01.jpg

miku03c02.jpg
import cv2

miku1 = cv2.imread('miku03c01.jpg',cv2.IMREAD_GRAYSCALE)
miku2 = cv2.imread('miku03c02.jpg',cv2.IMREAD_GRAYSCALE)

miku3 = cv2.absdiff(miku2,miku1)
cv2.imwrite('miku03c03.jpg',miku3)
miku4 = cv2.subtract(miku2,miku1)
cv2.imwrite('miku03c04.jpg',miku4)
miku03c03.jpg

miku03c04.jpg

จะเห็นว่าเมื่อใช้ cv2.absdiff() ส่วนต่างไม่ว่าจะเกินมาจากภาพไหนก็จะได้ค่าออกมาเป็นค่าบวก

ต่อไปลองใช้กับภาพสี

miku03c05.jpg

miku03c06.jpg
miku5 = cv2.imread('miku03c05.jpg')
miku6 = cv2.imread('miku03c06.jpg')

miku6 = cv2.absdiff(miku6,miku5)
cv2.imwrite('miku03c07.jpg',miku6)
miku7 = cv2.subtract(miku6,miku5)
cv2.imwrite('miku03c08.jpg',miku7)
miku03c07.jpg

miku03c08.jpg




การคูณหารรูปภาพ

เมื่อมีบวกลบก็ย่อมมีคูณหารด้วย ฟังก์ชันคูณใน cv2 คือ cv2.multiply() ส่วนฟังก์ชันหารคือ cv2.divide() หลักการก็เหมือนกับตอนบวกและลบ คือค่าจะไม่พ้นไปจากในขอบเขต 0 ถึง 255

ประโยชน์การใช้งานเช่นอาจสร้างตัวคูณที่จะทำให้เกิดการไล่สีต่างกันไปในแต่ละบริเวณ

ตัวอย่างเช่น มีภาพนี้อยู่

teto03c01.jpg

และลองสร้างภาพไล่สีแบบนี้ขึ้นมาเป็นตัวคูณ
arrx = np.ones([450,600,3])*255
arrx[:,:,1] -= np.arange(600)/5
arrx[:,:,0] = np.arange(600)/5
cv2.imwrite('a03c01.jpg',arrx)

a03c01.jpg

สามารถเอาภาพแรกมาคูณกับตัวไล่สีเพื่อจะให้ได้ภาพที่มีลักษณะไล่สีไปตามที่คูณได้

เพียงแต่ว่ามีความยุ่งยากอยู่เล็กน้อย เนื่องจากข้อมูลภาพมีค่าอยู่ในช่วง 0 ถึง 255 แบบนี้จึงไม่สามารถเอามาคูณกันได้เลย ควรปรับขนาดตัวคูณให้เป็นค่า 0 ถึง 1

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

ในที่นี้ทำให้เป็น float32 เหมือนกันไปด้วย แล้วลองนำมาคูณกันดู
arrx = arrx.astype(np.float32)/255
teto = cv2.imread('teto03c01.jpg').astype(np.float32)

teto2 = cv2.multiply(teto,arrx)
cv2.imwrite('teto03c02.jpg',teto2)

teto03c02.jpg

เวลาที่จะนำผลที่ได้มาใช้ต่ออีกนั้น บางกรณีอาจต้องเปลี่ยนกลับเป็น uint8 ขึ้นอยู่กับว่าจะไปใช้กับฟังก์ชันอะไรต่อ แต่สำหรับ cv2.imwrite() ซึ่งใช้บันทึกภาพลงไฟล์นั้นจะใช้ข้อมูลชนิดใดก็ได้จึงไม่จำเป็นต้องเปลี่ยนกลับก็ได้

ต่อมาลองเปลี่ยนมาเป็นหารดู
teto3 = cv2.divide(teto,arrx)
cv2.imwrite('teto03c03.jpg',teto3)
teto03c03.jpg

นอกจากนี้มีฟังก์ชัน cv2.pow() ไว้คำนวณยกกำลัง cv2.max() กับ cv2.min() เอาไว้ใช้คัดเอาค่าสูงสุดหรือต่ำสุด (คล้าย np.maximum และ np.minimum ของ numpy แต่ต่างจาก np.max หรือ np.min)




การผสมรูปภาพโดยถ่วงน้ำหนัก

ฟังก์ชัน cv2.addWeighted() ใช้นำภาพ ๒​ ภาพมารวมกันโดยถ่วงน้ำหนักตามที่กำหนด

วิธีใช้
cv2.addWeighted(x1, α, x2, β, γ)

ภาพใหม่ที่ได้จากการผสมจะมีค่าเป็น αx1+βx2

โดย
  • x1 คือ ภาพที่ ๑
  • α คือ น้ำหนักของภาพที่ ๑
  • x2 คือ ภาพที่ ๒
  • β คือ น้ำหนักของภาพที่ ๒
  • γ คือ ค่าที่บวกเพิ่มเติม
α+β จะเป็นตัวกำหนดว่าจะผสมเอาภาพไหนด้วยอัตราส่วนน้ำหนักเท่าไหร่ โดยทั่วไปจะให้ α+β=1 เพื่อให้ได้ความสว่างออกมาค่าอยู่ในช่วงระหว่างค่าของ ๒ ตัว

ลองใช้ ๒​ ภาพนี้เป็นตัวอย่าง

teto03c04.jpg

teto03c05.jpg

เอามาผสมกันด้วยน้ำหนักที่ต่างกันไป

teto1 = cv2.imread('teto03c04.jpg')
teto2 = cv2.imread('teto03c05.jpg')
# ผสม 2:8
teto3 = cv2.addWeighted(teto1,0.2,teto2,0.8,1)
cv2.imwrite('teto03c06.jpg',teto3)
# ผสม 5:5
teto4 = cv2.addWeighted(teto1,0.5,teto2,0.5,1)
cv2.imwrite('teto03c07.jpg',teto4)
# ผสม 8:2
teto5 = cv2.addWeighted(teto1,0.8,teto2,0.2,1)
cv2.imwrite('teto03c08.jpg',teto5)

teto03c06.jpg

teto03c07.jpg

teto03c08.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)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ