φυβλαςのβλογ
บล็อกของ 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
ภาษา 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月

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

ไทย

日本語

中文