用Python實作 Perspective Transformation 透視變換

  • 主要演算法
    1. 通常我們在自然背景拍攝一張照片時,或多或少都會有一些透視變形存在,而透視變形是由拍攝和觀看圖像的相對距離決定的,導致遠近特徵的相對比例有變化,產生了彎曲或變形,當然在本例子,我是刻意拍了一張比例歪斜的圖,想要驗證依照公式轉換會得到什麼效果。
    2. 依照解決方法,這樣的變形我們可以下列的聯立方程式來表示:

​x = ax’ + by’ + cx’y’ + d

y = ex’ + fy’  + gx’y’ + h

​其中x、y為你原始影像的座標系,x’、y’為校正後的座標系,

a、b、c、d、e、f、g、h為常數,表示變形關係。

  • 程式片段
  1. 定義高斯消去

2. 先定義4個點,找出矩陣

3. 照公式進行矩陣轉換,解出方程式,輸出結果就得到圖片了。

4. 另外我也參考網路範例,仿造OpenCV寫了一個轉置方法,後續比較一下兩種做法的差異。

  • 測試資料與結果
  1. 原圖是張刻意拍歪斜的照片,我標出四個白點,可以看到原始轉換區塊變形到接近菱形了。

2. 轉換後得到的結果如下,或許是因為變形幅度高,沒達到完整的轉換。

3. 參考網路實作Perspective Transformation做出來的效果,因為先做3D後轉2D,校正效果很好,只是程式寫得不好,有失真。

4. 使用Open CV做的效果

  • 討論
  1. 原本課堂講解的校正來源是一個平行四邊形,校正效果相當好,但在本次作用我使用一個不規則的多邊形來做轉換,雖然轉換結果不比原本的好,但還算可以得出資訊。
  2. 參考Perspective Transformation實做出來的程式做轉換,對於不規則多邊形轉換的效果很好,只是有失真,若用OpenCV轉出來的話就趨近完美了。
  3. 此類的校正可以應用在機器人自駕時的資訊辨認,或是在OCR應用上先把讀入的圖片做校正,在讀取資訊,可以減少判斷失誤。
  4. 在Deepfake影片偵測的研究中,許多論文的原理是把影像的Frame讀入後分析他是不是假造影像;如果在深度學習時,我先把人臉或是指定的擷取特徵做校正,可以把每個Frame的特徵變成一樣的構圖,就有機會分辨出他是不是造假的Frame:因為Frame造假是基於原本扭曲變形的畫面做變更,在校正後應該可以看出他和一般正常角度Frame的不同之處。

使用Python實作Image Enhancement

趁著學期告一段落,工作也在過年前的休息時間,趕快把之前的作業整理出來;這篇文章依然會是先前的影像處理課程所編寫的作業,在這裡不會用到深度學習或機器學習,而是使用傳統的影像處理概念來做,目的是為了瞭解原理。

本篇文章主要是探討如何通過二階微分,將影像邊緣強化。

  • 主要演算法
    1. 原圖雖然用的是彩色影像,但我在程式中有轉為灰階,所以實際增強是針對灰階影像增強。
    2. 把原影像先乘Laplacioan運算子做二階微分,可以得到影像的邊緣。
    3. 接著和原始影像相加,邊緣會被加強,但是因為原圖的雜訊也被放大,所以這裡的圖是有雜訊的加強,得到圖A。
    4. 接著原始影像用Sobel對X和Y做一階微分,可以得到影像邊緣圖B,接著對圖B做去雜訊。
    5. 再來把圖B的像素做標準化,每個像素會被做成0~1的數值,此時在平坦區(比較亮的地方)數值會較大,趨近1,邊緣(比較暗的地方)則趨近0。
    6. 我們把標準化的矩陣和圖A相乘,可以知道原圖依照比例被放大了,原先暗的像素方又變得更黑了,非邊緣的地方則會因為乘趨近1的值,故不太有大變化,結果我們會得到圖C,圖C就是加強後的結果。
  • 程式片段
    1. 先把圖片轉成灰階影像。

2. 定義Laplace運算子遮罩

3. 定義Sobel運算子遮罩,0是dY,1是dX

4. 定義Slide Window Filter降噪

5. 對原本影像乘Laplace運算子,二階微分得到邊緣,接著和原圖相加。

6. 把微分後結果和原圖相加,得到一張銳化的圖。

7. 接著對原圖做Sobel運算子,一階微分先做dX再做dY,然後用OR合成,也可以得到一張圖的邊緣。

8. 接著用Slide Window filter把雜訊降低。

9. 最後我們把這結果做標準化,取得一個0~1的矩陣,然後把矩陣和步驟3的圖相乘,圖的暗部被放暗,亮度則不太改變,因為暗部被變暗了,會感覺到邊緣變得銳利。

  • 測試資料與結果
    1. 原圖,將他模糊後轉成灰階。

2. 做Laplace運算後得到邊緣。

3. 和原圖相加,但感覺到雜訊也放大了

4. 對原圖X、Y做Sobel微分後,做合成,得到一個較為銳利的邊緣。

5. 使用Slide Window Filter把雜訊去掉。

6. 接著把模糊後結果乘上前面的圖,得到邊緣增強的結果。

  • 討論
    1. 把Sobel微分後的結果做標準化後,再乘上和二階微分後的圖,可以有效讓暗部變暗,因為標準化結果介於0~1之間,亮部*1~0.5之間,只會感到些許變暗。
    2. 但是這樣的方法會讓原本的圖變得比較暗,暗部變的更暗,邊緣會更明顯,但亮部受到乘數影響,也明顯感覺到變暗。
    3. 依據課堂上提到一個好處,我們把邊緣做模糊消去雜訊後再做標準化,因為雜訊已經被去掉了,最後再去和圖相乘,可以避免雜訊放大。
    4. 延伸一個思考問題,若是我們處理彩色影像時,對R、G、B個做一次上述步驟的處理,是否也能把彩色影像的影像做增強呢?

使用Pytorch實作卷積網路(CNN)做多分類圖片識別

這篇文章是我在修習影像處理時做的作業之一,主要是想訓練一個Model來識別輸入的圖片是在室外環境,還是室內環境,為了完成這個目標,我使用Pytorch實作了卷積神經網絡(Convolutional Neural Network)簡稱CNN,搭配網路上的資料集進行訓練,最後將我隨意亂拍的照片輸入做辨識,發現效果還不錯。

以下節錄我當時整理出來的作業內容,大略解釋了演算法的概念,訓練過程,和最後檢討,因為是作業,故有很多是比較學生角度來解釋整個事件。

  • 主要演算法
    1. 室內、室外乍看是二分類,可以用線性回歸(Linear regression)的方式來做,但是考量室內室外只是眾多場景一種,故我用多分類(Multi label classification)的方式開發程式,並用CrossEntropy計算Loss。
    2. 會採用多分類(是因為我單純認為用二分法來識別兩個場景不是很好的方法,最後預測結果會落在0~1之間,若用0.49中位數來分辨0和1,感覺有點奇怪。
    3. 本次使用Pytorch實做一個簡單的CNN網路架構,CNN最主要會由Convolutional和Maxpool兩種層所組成,激勵函數是Relu,然後最後要用線性層(Fully Connected)輸出預測結果。
    4. 初次訓練只用了2層CNN搭配1層的線性層,但訓練的Loss率沒有想像好(低),故再次修改用4層CNN搭配3層的線性層,一方面想看看線姓層的降維幅度是否也會改善Loss率。
    5. 在訓練、驗證和測試資料夾中,我都定義了Indoor和Outdoor資料夾,同時也是以此當作標籤功能。
    6. 最後輸出時,我們去觀察哪個Label的得分最高,就可以知道他是屬於哪個類型的,假定我規劃0是室內,1是室外,當我發現0的得分是0.9991,1的得分是0.1234,那則代表這張圖是室外。
    7. 訓練過程中,一開始Model會去預測一個分數,但我們會依照正確的分類給他真正的分數,接著算出Loss後,經過backward去更新權重,Model就會知道屬於這張圖真正的分數應該是多少,因此一開始Loss極高是正常的,但會隨著訓練逐漸降低,但是Loss如果隨著訓練持續亂跳亂跳的,那有可能代表圖片的Label太雜亂,Model會精神錯亂,這時候你就必須回頭去檢視你的資料集正不正確。
  • 程式片段
    1. 定義網路,4層CNN搭配3層的線性層

本次我是使用多分類方式來做,故採用CrossEntropy的Loss計算。

Crossentropy主要用在計算多分類的交叉熵。

本次訓練15次,主要記得開啟訓練模式、梯度歸零和更新等,Model會預測這張圖是屬於哪個類型,一開始Model處於亂猜階段,隨著我們去算出他的Loss率,然後再backward和用step做權重更新後,Model就會學習到正確的分類。

訓練時需把Loss率反向傳播,這樣才能讓整個網路知道辨識的錯誤率為何。

驗證也是15次,記得開啟評估模式就好,因為在這裡我們想做的是驗證模型準確率,不需要再把驗證出來的結果反向傳播回模型了。

注意開啟評估模式,藉此來了解你的Model能力。

附上網路的形狀

本次作業規劃的網路形狀。
  • 測試資料與結果
    1. 訓練和驗證資料集主要來自https://diode-dataset.org/的Dataset,先人工整理出Train和Valid的資料夾,並在其中切分出Indoor和Outdoor,作為輔助,此外我也定義了Indoor是標籤0,Outdoor是標籤1。
    2. 訓練15次,可參考下圖,藍色為訓練的Loss率,橘色是驗證的Loss率,訓練的Loss率原本逐漸下降,但到後期又急速攀升,初步判斷是訓練Label雜亂造成?但因為訓練次數不夠多,如果訓練100次仍這樣亂跳,那就有可能代表圖片的Label太亂,造成loss計算不清楚;而驗證的Loss率則在訓練完第1次後趨於平緩。
訓練和驗證時,Model的Loss率變化。
  1. 在網路輸出時主要會得到一個2元素的Tensor,在這裡我沒多做轉換,若是索引0的機率高,則代表這圖是Label 0 Indoor,1的話則是Outdoor。
    1. 訓練15次後進行測試,Loss率0.31左右,得到的成功率是90多%,感覺好像有點厲害,相較先前使用3層CNN搭配一層線性層,有不錯增長。
圖片實際預測結果,此10張都是我刻意挑選用來混淆Model的圖片。
  • 討論
    1. 本次的測試資料集我是用自己拍的10張圖,其中有些我刻意選擇比較怪異的構圖,譬如在樹叢中拍攝帳篷,或是在有雨棚的路邊攤拍攝,並當作是Indoor照,企圖影響網路的判斷。
    2. 承上,之所以會這樣做是因為我認為類神經網路在擷取特徵時,應該是以構圖和色調在抓室內和室外的差別,才故意找一些界定模糊的資料。
    3. 訓練的Loss率亂跳一方面或許和原始資料集也有關,譬如有放在桌上的帆船模型,照理說和室外的海上的船是蠻像的,不過若是加大訓練次數,有機會看到Loss率逐漸趨緩,因為Model將會逐漸分辨出室內和室外該有的色調,進而判斷出正確結果,而不再是被物體的形狀限制。

若有機會,會想嘗試把RNN加入到網路中,藉此了解當面對複雜特徵且數量眾多的圖片,Model的學習能力是否有加分效果,當然可預想的是RNN效果未必會比較好。

若是你對本篇文章的原始碼有興趣,可以參考我的Github連結

你今天NFT了嗎?

你今天NFT了嗎?最近台灣科技新聞中,先是區塊鏈,再來是元宇宙,最近最火的莫過於於NFT了,不管是文創業者,譬如霹靂布袋戲推出的魔吞十二宮NFT,或是張泰山的球員卡NFT,在在都掀起一波熱潮。

NFT全名Non -Fungible Token,翻成中文就是「非同質化代幣」,跟比特幣「非同質化代幣」不太相同,NFT強調的是獨一無二,只會有一份,所以在每個NFT上都會有一個編碼,用來做為識別。

舉個淺顯易懂的例子好了!

就比特幣「非同質化代幣」來說,今天你出門參加聚餐,身上帶了一張千元鈔票,跟朋友碰面時,不巧朋友想買東西卻忘記帶錢,就跟你借一千元,等遇到提款機時,才領了一千元給還你,此時對你來說,你皮夾裡依然有一張千元鈔票,這也是貨幣的本質,來來去去的,我們講究的是他等同的價值,你身上有一千塊,不管你的一千塊是一張鈔票,還是2張五百元鈔票,還是10張一百元鈔票。

但就NFT來說,如果硬要用剛剛鈔票的例子來講,當朋友領了一千元還你,這張一千元已經不是當初的一千元了,因為鈔票上的編號並不相同;這樣講可能太玄,簡單來說,NFT可以看作是一個所有權,譬如房屋、車輛或是土地的所有權,你必須把這權狀握在手上,你才能證明這房屋或土地是你的。

若是你的權狀弄丟了,那你就倒大楣了。

而透過區塊鏈的加密技術,當你擁有一個NFT,關於這個NFT的所有權則會被寫入到區塊中,你會有一把金鑰(Token)來解開所有權的紀錄資訊,證明這個NFT是你的;再科普一點,當你擁有一塊土地,理所當然你就會有一張土地權狀,這張土地權狀會由當時的政府機關頒發,如果你夠謹慎,你會把他鎖在保險箱中,只是在區塊鏈構成的數位世界中,你所擁有的權狀是由平台頒發,而權狀資訊則是被加密後所在網路上的不知名地方。

之所以說不知名,也是區塊鏈的特性之一,匿名。

close up photography of yellow green red and brown plastic cones on white lined surface
Photo by Pixabay on Pexels.com

一句話快速了解NFT,NFT就是透過區塊鏈做出來的數位版權技術。

也就所以你會發現,一開始踏入NFT的作品,多偏向數位資料,譬如影片、照片,至少以現在大部分新聞報導的是如此,事實上只要可以數位化的內容都可以被做成NFT,例如程式碼,WWW的發明者就把他當時的原始碼做成NFT銷售,而Twitter的開發者也把他的第一則推文作為NFT銷售。

在現今市場的大熱潮中, NFT的製作和銷售達到蠻有趣的高峰,大家都爭相把自己的數位產品做成NFT,同時也是把公有區塊鏈又進一步往普羅大眾推進,在現實資金和虛擬貨幣接軌之後,開始實現另一波的資產流動,也逐步往公開、公平的步伐邁進。

在Colab Notebooks上定義並訓練Yolov4 Model (以水果辨識為例)

Yolo (You only look once)可以說是最知名的即時辨識模型了,不僅效果好,速度也快,當初的論文可以參考這裡,現在一路也出到V4了,你可以在訓練資料上定義不同的圖片和標籤,以此訓練模型,就可以輕鬆達到辨識新目標的效果。

當然我這樣講是有點浮誇,你必須注意一下你的電腦能不能如願把訓練跑完。

在這次的訓練中,我嘗試在Colab Notebooks上安裝,並訓練Yolov4,主要是為了省去GPU這個環節,因為平時我的工作筆電是一台Mac,想要跑完訓練是真的需要一點時間的;另外為了完成訓練,首先我必須找到一份資料集,再來我必須製作自己的YOLO  Format標記檔案,提供給Yolov4進行訓練。

資料集的部分,我使用的是Kaggle上的Fruits fresh and rotten for classification資料集,這其中包含了新鮮蘋果、香蕉、橘子和腐敗的蘋果、香蕉和橘子等六種類型的圖片,如何在Colab Notebooks上下載Kaggle資料集的話可以參考我這篇文章。

至於如何把Kaggle資料集轉成Yolo format的部分,可以參考這篇文章,在這裡我捨棄了用LabelImg,也捨棄了Yolo Marker,為什麼呢?純粹是因為我懶,純粹是因為我想盡量把工作自動化,雖然可能割捨掉一些正確性。

在準備好資料集和Yolo Format的標記檔案後,我們可以開始接著作模型下載和訓練的工作,首先我們要先從AlexeyAB下載darknet,這是一個使用C語言實作Yolo的共用函式庫,但是可以提供Python調用;當初是從Yolo的原始作者那fork出來的分支,因為原作者已經不維護原始程式碼了,所以網路上大多用這個版本在訓練和實戰。

首先,我們下載darknet。

當darknet下載完成後會在Colab上建立一個目錄,這裡要注意,後面訓練、打包時,都會在這個目錄底下進行。

當Make時,或模型訓練時,會需要持續寫入權重(weights),所以我們需要先把目錄權限改一下,開放寫入。

在Make打包之前,我們需要改一下組態檔案,這將影響我們後續打包完的模型如何運作,在這裡我們用sed來修改組態,sed 是「stream editor 」的縮寫,顧名思義是進行串流(stream) 的編輯。無論是在編寫shell 的或處理STDIN 的時候,當有需要進行字串取代、複製、刪除,就可以直接下命令調整,當然你也可以直接透過編輯器修改;這裡我們有幾個東西要調整。
  1. OPENCV=1,啟用OPENCV,注意,你必須先安裝OpenCV。
  2. GPU=1,啟用GPU,注意,你的Coblab要先啟用GPU。
  3. CUDNN也需要啟用,他是基於CUDA的DNN函式庫。如果你用的是自己的電腦,也有自己的GPU,可以找一下顯卡可以支援到什麼程度,然後修改ARCH,ARCH主要是指定框架裡面用到的GPU資訊,在某些狀況下會導致你的YOLO無法編譯,詳細狀況可以參考這篇文章。

接著進行Make,基本上做這些事情速度都很快,麻煩的是後面的訓練。

打包完成後,就可以開始訓練了,在訓練時,我傾向使用網路上已經訓練過的模型(Pre-Trained Model),再來做一次訓練,這樣的效果和識別能力會更好,因為在模型裡面已經有其他人訓練好的權重,如此一來,我們也可以用比較少的訓練資料量,來達到一定的辨識效果。

另外我會用yolov4-tiny.cfg做為訓練用的設定組態,之所以不用yolov4.cfg來訓練,是因為yolov4.cfg的batch數比較多,在Colab Notebooks上訓練很容易記憶體不足,或是說根本就練不起來。

訓練時我會引入前面下載的預訓練模型,需要用到一點時間訓練,基本上幾小時是跑不掉的。

訓練完畢的話,也有語法可以測試,注意一下他會在測試圖片標記出辨識結果。

圖片的辨識訓練其實不會很複雜,訓練資料集方面,也不需要把圖片都轉成同樣大小(這也是為什麼你在產生Yolo format的label資料時,需要取的是比例的相對位置,而不是像素的絕對位置),另外需要注意的是cfg設定檔中的filter數量,這個數值會和你定義的class數量相關,計算公式為filters=3*(classes+5),譬如你在資料集中定義了3個類型(classification),那filters則會是(3+5)*3=24。

後面我會花一點時間整理Yolo的做法和基本原理,並且附上我的些許心得,如此一來才能算真正掌握了Yolo這套框架。(待續)

January 14, 2022 at 04:33PM