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



แก้ปัญหาที่บางครั้งภาพที่อ่านใน python ถูกหมุนหรือพลิกกลับด้าน
เขียนเมื่อ 2024/06/16 12:48
 

ใครเขียนโปรแกรมภาษาไพธอนเพื่อจัดการภาพแล้วเคยเจอปัญหาว่าภาพที่อ่านเข้ามามันถูกหมุนหรือพลิกกลับด้าน ไม่เหมือนกับที่ปรากฏในเว็บเบราว์เซอร์หรือโปรแกรมอ่านภาพทั่วๆไปบ้างหรือเปล่า?

บทความนี้จะอธิบายสาเหตุของปัญหาที่เกิดขึ้นนี้และวิธีการแก้ปัญหา

เนื้อหานี้ได้เขียนเป็นภาษาญี่ปุ่นลงในเว็บ qiita ด้วย
 → 読み込んだ画像が回転したり反射したりする原因と対策



ปัญหาที่เจอ

ขอยกตัวอย่างโดยใช้ภาพนี้


irelcp.jpg


ลองโหลดไปลองเปิดดูกันได้ เช่นลองเปิดแล้วก็แสดงด้วย matplotlib แบบนี้
import matplotlib.pyplot as plt

rup = plt.imread('irelcp.jpg')
plt.imshow(rup)
plt.show()

ก็จะได้ภาพที่ถูกหมุนทวนเข็มนาฬิกาไป ๙๐ องศาแบบนี้



ทำไมถึงเป็นแบบนี้? เกิดอะไรขึ้น? ภาพนี้มีอะไรบางอย่างผิดพลาดหรือ? หรือว่าผีหลอก!?

ใครที่เจอแบบนี้ก็คงตั้งข้อสงสัยไปต่างๆมากมาย แต่ที่จริงแล้วนี่ไม่ใช่ความผิดพลาดของตัวโปรแกรมหรอก ลองเปิดอ่านดูด้วยวิธีอื่นเช่น skimage, imageio หรือ PIL (pillow) ดูก็ได้ผลเหมือนกัน

skimage
from skimage import io

rup = io.imread('irelcp.jpg')
print(rup.shape) # (600, 450, 3)
(shape ในที่นี้แสดงขนาดของอาเรย์ภาพ ซึ่งแสดงเป็น (ความสูง, ความกว้าง, สี))

imageio
import imageio

rup = imageio.imread('irelcp.jpg')
print(rup.shape) # (600, 450, 3)

PIL
from PIL import Image

rup = Image.open('irelcp.jpg')
print(rup.height,rup.width) # 600 450

ไพธอนนั้นมีวิธีการที่หลากหลายในการเปิดอ่านไฟล์ แต่ว่าวิธีการอ่านส่วนใหญ่ที่นิยมใช้กันนั้นต่างก็อ่านภาพนี้ออกมาได้แบบนี้กันหมดเลย

ดังนั้นเพื่อที่จะได้อ่านให้ถูกต้องเราจำเป็นต้องรู้สาเหตุและทางแก้ไข




สาเหตุของปัญหา

ที่จริงแล้วปัญหานี้เกิดกับภาพบางส่วนที่มีข้อมูล exif

exif คืออะไรนั้นได้เคยอธิบายไปในบทความก่อนหน้านี้แล้ว ในที่นี้ขอไม่อธิบายรายละเอียด หากสนใจสามารถอ่านได้ใน

จัดการข้อมูล exif ในไฟล์รูปภาพด้วย PIL และ piexif

ภายในข้อมูล exif นั้นมีข้อมูลส่วนที่เรียกว่า orientation คือค่าที่บอกว่าจะมีการปรับเปลี่ยนการจัดวางรูปภาพอย่างไร

อย่างเช่นในกรณีภาพที่มีปัญหานี้ เกิดจากการที่ข้อมูล orientation ได้ระบุว่าให้ "หมุนภาพตามเข็ม ๙๐ องศา"

ดังนั้นที่จริงแล้วก็คือภาพที่เห็นว่าหมุนทวนเข็มไป ๙๐ องศาที่เห็นแสดงเมื่ออ่านด้วยไพธอนนี้คือภาพเดิมจริงๆที่ยังไม่โดนปรับแก้การจัดวางตามข้อมูลที่ระบุใน exif

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

ข้อมูล exif นี้สามารถดูได้โดยการปิดภาพด้วยมอดูล PIL แล้วใช้เมธอด ._getexif()
from PIL import Image

rup = Image.open('irelcp.jpg')
print(rup._getexif()) # {274: 6}

ค่า 274 เป็นรหัสของข้อมูลส่วน orientation ส่วน 6 เป็นรหัสแสดงการจัดเรียง มีความหมายดังนี้

1 ไม่เปลี่ยนแปลง
2 กลับซ้ายขวา
3 กลับซ้ายขวาหน้าหลัง (หมุน ๑๘๐ องศา)
4 กลับบนล่าง
5 กลับระหว่างซ้ายล่างกับขวาบน
6 หมุนตามเข็ม ๙๐ องศา
7 กลับระหว่างซ้ายบนกับขวาล่าง
8 หมุนทวนเข็ม ๙๐ องศา

ภาพโดยส่วนใหญ่จะเป็น 1 คือไม่จำเป็นต้องไปทำอะไร แต่ว่าถ้าเจอเลขอื่นก็จำเป็นต้องนำภาพมาแก้ก่อนจึงจะใช้งานได้อย่างถูกต้อง

ภาพที่เป็นแบบนี้ส่วนใหญ่จะเป็นภาพถ่ายที่ถ่ายด้วยแอปพลิเคชันบางอย่าง หรือถูกปรับแก้ด้วยโปรแกรมบางอย่าง ซึ่งไปพลิกภาพโดยการระบุข้อมูล orientation ใน exif แทนที่จะพลิกภาพจริงๆ

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



สำหรับวิธีการแก้นั้นต่อไปนี้ขอแนะนำ ๒ วิธีด้วยกัน




การแก้โดยใช้ opencv

มอดูลที่กล่าวมาข้างต้นส่วนใหญ่ของไพธอนนั้นเมื่อเปิดอ่านภาพจะไม่มีการปรับแก้ภาพตามข้อมูล exif ให้อัตโนมัติ แต่ว่าก็มีบางมอดูลที่ปรับแก้ให้เลยเหมือนกัน นั่นก็คือ opencv (cv2 นั่นเอง ดังนั้นถ้าใครเปิดอ่านด้วย opencv อยู่แล้วก็ไม่จำเป็นต้องมากังวลเรื่องนี้เลย

เกี่ยวกับเรื่องการใช้ opencv ในไพธอนถ้าใครสนใจสามารถอ่านได้ในบทความนี้

opencv-python เบื้องต้น
ลองเปิดอ่านดูโดยใช้ opencv กันเลย
import cv2
import matplotlib.pyplot as plt

rup = cv2.imread('irelcp.jpg')
print(rup.shape) # (450, 600, 3)
plt.imshow(rup)
plt.show()



จะเห็นว่าภาพถูกแสดงหันถูกทิศ ไม่หมุนเพี้ยนไป แต่อ้าว ทำไมสีมันดูแปลกๆ?

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

ดังนั้นถ้าใครคิดแค่ว่าจะอุตส่าห์ใช้ opencv เพื่อเปิดอ่านรูปภาพละก็อาจจะไม่ค่อยสะดวกนัก ต้องมาแปลงสีกลับก่อน เช่นทำแบบนี้
rup = rup[:,:,::-1]

นอกจากนี้แล้วยังมีข้อเสียอีกอย่างคือ opencv อ่านไฟล์ที่ชื่อเป็นภาษาไทยหรือภาษาอื่นๆไม่ได้ จึงต้องมาหาทางรับมือกับตรงนี้อีกที

ดังนั้นแล้ว ขอแนะนำอีกวิธีมากกว่า นั่นคือการใช้ PIL




การแก้โดยใช้ PIL

PIL นั้นมีฟังก์ชัน ImageOps.exif_transpose อยู่ แค่ใช้ฟังก์ชันนี้ภาพก็จะถูกปรับพลิกหรือหมุนกลับตามข้อมูล orientation ของ exif

เราอาจลองเขียนฟังก์ชันสำหรับอ่านไฟล์ไว้ให้ตรวจดูว่าภาพนี้มี orientation เป็น 2 ขึ้นไปหรือเปล่า ถ้าเป็นก็ใช้ฟังก์ชันนี้ทำการปรับแก้ซะ
import numpy as np
from PIL import Image,ImageOps

def imread(f):
    rup = Image.open(f)
    exif = rup._getexif()
    if(exif and 274 in exif and exif[274]!=1):
        rup = ImageOps.exif_transpose(rup)
    return np.array(rup)

แล้วก็ลองเอาฟังก์ช้นนี้มาใช้งานดู
rup = imread('irelcp.jpg')
plt.imshow(rup)
plt.show()

เท่านี้ก็น่าจะได้ภาพที่แสดงผลอย่างถูกต้องแล้ว

หรืออาจเขียนให้แก้แล้วก็บันทึกทับไฟล์เดิมไปเลย แบบนี้ครั้งต่อไปก็ไม่ต้องมาห่วงเรื่อง exif แล้ว
f = 'irelcp.jpg'
rup = Image.open(f)
exif = rup._getexif()
if(exif and 274 in exif and exif[274]!=1):
    rup = ImageOps.exif_transpose(rup)
rup.save(f)



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

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

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

หมวดหมู่

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

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

目录

从日本来的名言
模块
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
机器学习
-- 神经网络
javascript
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



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

  查看日志

  推荐日志

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