跳到主要內容區塊

ntuccepaper2019

技術論壇

用python學習數位影像處理-第一篇
  • 卷期:v0066
  • 出版日期:2023-09-20

作者:陳俊佑 / 臺灣大學計算機及資訊網路中心程式設計組


連國中生都在玩Python數位影像圖學,身為社畜的我們再不學就落伍了!

 

前言

大家好,我是柚子哥,現在最火紅的軟體無非是python,由於Big Data已然成為現今顯學,大家無不想從無限的數據中掏金,又或是利用機器深度學習中算出最理想的股票投資組合數據,而Python符合簡單易學好上手,擁有眾多的現成套件可以使用,自然就有很多程式素人紛紛湧入Python的懷抱,不管你讀的是甚麼科系,資管、動科、植微、醫材、化學、物理、中文系、外文系…通通都學Python就對了,讓你成為最強的斜槓青年。

 

Python的簡單易學可說是有目共睹,某次跟一位在某國中當資訊代課老師的朋友聊天,他說現在他們學校有開始教國中生學習寫Python,身為社畜的我不學點Python真的太慚愧,因此,今天跟大家分享用Python玩入門等級數位影像圖學,介紹一些Python常用於處理影像的套件給大家。

 

我自己以前也是玩數位影像圖學,不過當時研究數位影像圖學的程式是用MATLAB去寫的,MATLAB語法好學、開發環境好用以及強大的矩陣運算,也是有目共睹,重點是MATLAB也有很完整的圖像處理工具箱(Image Processing Toolbox)可以做圖學的分析及演算法開發,不過畢竟MATLAB是要付費的軟體,不是每個人都有辦法負擔的起,因此使用完全免費的Python做數位影像圖學研究就變成是程式素人不錯的選擇。

 

基礎開發環境架設

  1. 我是用免費的Visual Studio Code開發平台(https://code.visualstudio.com/),去寫Python程式,相信大家對VSCode都不陌生,初學者可至官網去下載。

 

20230920_006608_01

圖1.Visual Studio Code官網

 

  1. 下載Python並安裝,(https://www.python.org/downloads/)。

 

20230920_006608_02

圖2.Python官網

 

  1. 打開Visual Studio Code到延伸模組去搜尋Python,並安裝。

 

20230920_006608_03

圖3.安裝延伸模組

 

  1. 在Visual Studio Code右下角打開選擇延伸模組,選擇python模組。

 

20230920_006608_04

圖4. Visual Studio Code右下角

 

20230920_006608_05

圖5.選擇python模組。

 

  1. 在檔案總管新增一個檔案helloworld.py,程式碼寫print (“HelloWord”),存檔後案【Ctrl + Shift + `】開啟終端機,在終端機輸入python helloworld.py,測試python語法可以執行,執行成功終端機就會跳出helloworld字樣,就代表環境建好了。

 

20230920_006608_06

圖6.測試python環境

 

常用影像圖學套件

  1. OpenCV(Open Source Computer Vision Library),早期用C++開發數位影像演算法的人應該對OpenCV不陌生,這是由英特爾開發的免費資料庫,他也有Python的套件可以用(https://pypi.org/project/opencv-python/),只要在終端機輸入pip install opencv-python,就會自動安裝好。

 

20230920_006608_07

圖7.opencv-python

 

20230920_006608_08

圖8.安裝圖

 

  1. Nump (https://numpy.org/)是提供大量陣列運算的功能還有數學函式,會安裝這套件是因為一張影像讀取出來通常是由RGB三個色板組成,由RGB轉成其他色彩空間(例如:HSV色彩空間),再去做影像強化或是抓特徵值去辨識等等,因此矩陣運算就對影像處理很重要。安裝方式同樣也是在終端機輸入pip install numpy,就會自動安裝好。

 

20230920_006608_09

圖9.NumPy官網

 

  1. Pillow(https://pypi.org/project/Pillow/)是Python知名影像處理函式庫PIL(Python Imaging Library)的後繼者。

 

載入影像

首先我們要再載入影像前先Import openCV。

 

20230920_006608_10

圖10.Import openCV。

 

再來用cv.imread(filename [,flags])的語法去讀取圖片。

filename是指圖片的檔案名稱含路徑,如果沒有指定路徑就把照片跟程式碼放同個資料夾就好,flags則是讀取模式,共13種讀取模式,詳細可以見官網https://docs.opencv.org/3.4/d8/d6a/group__imgcodecs__flags.html#ga61d9b0126a3e57d9277ac48327799c80查看,如果省略掉flags不寫則會預設成「IMREAD_UNCHANGED 」模式。

 

20230920_006608_11

圖11.讀取圖片。

 

最後使用cv.imshow(winname, mat)的語法讀取圖片,Winname是開啟照片視窗的名稱,mat則是影像的數據。

 

20230920_006608_12

圖12.顯示圖片。

 

但運行程式碼後會發現照片快速顯示一下就跳掉了,所以我們還需要額外一個暫停的程式碼,讓圖片顯示暫停一下。

 

20230920_006608_13

圖13.暫停

 

這樣子我們一連串從讀取照片到秀照片的流程就完成,寫影像處理演法就是在讀取跟顯示的程式碼之間加入我們自己的演算法。

 

20230920_006608_14

圖14.整套流程

 

灰階影像

我們在上面有介紹過讀取照片時,有說過官方網站有寫13種讀取模式,其中一個是「IMREAD_GRAYSCALE」灰階模式,顧名思義讀取的照片會轉成灰階。可不要小看灰階影像醜醜的沒什麼用,灰階影像在數位影像圖學中使用中可是很儣的,我們看到的彩色影像是由0~255的R、G、B三色板去組合而成,但色彩常因為現場的光線亮度不同、燈泡色溫不同而使R、G、B三色板產生劇烈的變化,所以轉成灰階圖片時反而更能夠算出穩定的影像梯度(Image gradient),越穩定的梯度值使用Canny邊緣偵測演算法(Canny edge detector)時則會越穩定偵測到邊緣,因為Canny邊緣偵測是用影像梯度去評估邊緣。而灰階影像還有另一個好處是減少運算量,同上所述彩色的影像是RGB三個0~255的色板組成,所以共有256(R)X256(G)X256(B)= 16777216種顏色的變化,但灰階範圍就0~255就256個變化而已,大幅減少每個像素的運算量,對於即時性的影像辨識運算速度特別重要,舉例來說一台智慧無人車行駛在路上,要辨識馬路上有沒有突然衝出來的行人或動物,辨識效率一定要非常的快,才能及時剎車殘不致造成傷亡,不過現在晶片越做越強,減少運算量優勢可能就會消失了。

 

關於灰階影像,有些影像圖學課本會定義說當RGB三個值都一樣時就稱為灰階像素,我們把彩色圖片轉成灰階就是把RGB三個像素做等量化的一個過程,而要把有16777216種顏色的彩色影像轉成只有256階灰階影像的方式,在數位影像學課本上最常見的有4種演算法,分別是分量法、最大值法、平均值法、加權平均法。每一種灰階的方法產出的圖片效果都不一樣,可以依照自己研發的演算法挑選出最適合的灰階手段,不過最常見的應該是加權平均法,每個方法詳細說明如下。

  • 分量:其實就是把RGB三個色板拆開來各自當成灰階圖片來用,也就是RGB三個分量的亮度作為灰階值。採分量法完全不用運算,有三總種灰階的影像可以選出自己最需要的其中一種去使用。公式如後GrayR=R、GrayG=G、GrayB=B。
  • 最大值法:最大值法就跟字面上意思一樣,把RGB三個值拿出來比較,最大的那一個值去同化,公式如後Gray=MAX(R,G,B)。
  • 平均值法:平均值法也是跟字面上意思一樣,就是RGB三個值加總除以3就完成同化,公式如後Gray=(R+G+B)/3。
  • 加權平均法:這個方法就比較有趣,他是根據人的眼睛對RGB三種顏色不同的敏感度去做加權後平均,因此也是大多數轉灰階函數的預設方式,公式如後Gray=0.299*R+0.587*G+0.114*B。

 

那我們就用純黃色(R=255,G=255,B=0)依照上述演算法一一實驗算出之後所得出的灰階影像。

 

20230920_006608_15

圖15.純黃色(R=255,G=255,B=0)

 

  • 分量法:一張純黃色的圖分成三種灰階,分別是R(255)、G(255)、B(0),可以看到就是2張白色1張黑色。

 

20230920_006608_16

圖16.分量法

 

  • 最大值法:抓出最大值,所以純黃色變灰階就會變成跟白色一樣。

 

20230920_006608_17

圖17.最大值法

 

  • 平均值法:依公式算出來(255+255+0)/3=170,灰階影像看起來就有感多了,不會像上面的算法,黃色只會變成黑或白。

 

20230920_006608_18

圖18.平均值法

 

  • 加權平均法:依公式算出(255*0.299+255*0.587+0*0.114)/3=75,純黃色算出來的灰階影像更暗一點。

 

20230920_006608_19

圖19.加權平均法

 

說了這麼多的灰階影像,我們還是要使用一下「IMREAD_GRAYSCALE」灰階模式,使用後的效果如下圖所示。

 

20230920_006608_20

圖20.灰階照片

 

但其中官方文件卻寫了一小段話,使用「IMREAD_GRAYSCALE」灰階效果可能會跟色彩空間轉換函數「cvtColor()」轉換出來的實際灰階效果不同。

 

20230920_006608_21

圖21.官方說明

 

這是為什麼呢?明明都是OPCV函數轉成灰階,但實際效過確有可能不同,官網並未詳述,但經過我們好朋友谷哥大神搜尋,在stackoverflow網站上就有人說明了原因,網站網址:(https://stackoverflow.com/questions/29548128/grayscale-conversion-algorithm-of-opencvs-imread),大家可以自己進去看。大致上是說jpeg影像用「IMREAD_COLOR」讀取圖片時會通過「JpegDecoder as JCS_RGB」解碼取得三個通道的影像,再由「icvCvt_RGB2BGR_8u_C3R()」函數去轉換R跟B的空間來取得RGB格式的彩色影像,而「IMREAD_GRAYSCALE」是由「JpegDecoder as JCS_GRAYSCALE」來解碼成一個通道影像,其他顏色轉換和其他預處理/後處理細節都是交由「libjpeg」去做處理,完全沒有使用內部「cvtColor」函數來轉換。

所以為了保持演算法實驗的一致性建議初始影像不要用imread的參數去轉灰階影像,全部影像統一用「cvtColor()」函數去轉灰階。

 

20230920_006608_22

圖22.用「cvtColor()」函數去轉灰階

 

推薦「cvtColor()」函數去轉灰階的原因有幾個,第一個是實測過後是使用 加權平均法去轉灰階影像,而imread實測跟加權平均法得到的灰階影像數據有一定的出入,還要再實際看看libjpeg的演算法才知道,所以推薦「cvtColor()」函數去轉灰階,第二個理由則是這兩個轉出來的通道數不一樣,imread轉出來灰階影像只剩一個通道,cvtColor()函數轉出來是維持三個通道,我們用shape語法去看他的通道數就知道,如下圖,可以清楚知道實測結果。

 

20230920_006608_23

圖23.以shape看通道數

 

20230920_006608_24

圖24.查看通道數結果

 

儲存圖檔

在我們完成演算法之後一定會想把跑過演算法的圖片存下來,這時就要用到cv2.imwrite(filename, image)的語法了,但礙於本篇字數快達到上限,故我這邊簡單Demo,下次有機會在開新的文章跟大家說明。

 

20230920_006608_25

圖25.寫入檔案

 

結語

在數位影像處理的領域非常廣,有很多有趣的東西可以研究以及分享,從早期人臉辨識到現在deepfake技術,都是數位影像圖學研究的範圍,真是令人深深著迷於其中,不過礙於每篇字數有3000字的上限,所以我就分期分段跟大家分享一些數位影像圖學的知識,希望大家會喜歡。

感謝大家耐心的閱讀,如內容有誤請不吝指正,讓我知道最新最正確的資訊,也可以分享給大家少走冤枉路,謝謝。

 

參考資料