iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 1
2

前言:影像處理是把數位的影像作前置的加工及處理,以便日後的分析。
人可以透過眼睛傳遞影像,並利用大腦的分析;而鏡頭就仿如人類的眼睛,當數位影像傳遞給電腦時,無時無刻也有機會產生雜訊,而且電腦在處理影像時並沒有人類大腦般聰明,因此需要影像處理,把雜訊濾掉、邊緣銳化...,把重要的影像轉換成電腦的角度,以便日後處理(影像處理完後,下一個課題是影像識別)

開發環境:C-Free 5
注: 1)以下只提供演算法程式,沒有提供main檔案
  2)原始圖檔取自網路
================分格線================

https://ithelp.ithome.com.tw/upload/images/20171218/20107818vzIIiwZbhs.png
圖1)上圖為 影像原始影像
解釋:在圖1中,我們可以看出從負X 至 正X之間的f(x)為一個漸變層
在電腦視覺最喜歡瞬間變化的影像,相反而言,最害怕遇到漸變層的影像
舉個例子:1000度近視的人,沒有戴眼鏡所看到的東西,就仿如電腦所看到的視角,一片模模糊糊的;因此而要為電腦戴上眼鏡(影像處理),而是次所解說是用Laplacian微分法作處理。

https://ithelp.ithome.com.tw/upload/images/20171218/20107818pTudzlH4vD.png
圖2)上圖為 經過1階Laplacian微分後的影像
解釋:我們可以透過圖2得知,1階微分後,f(x)由一個漸變層,變成近似脈衝的波形,而這個脈波就是影像中物體與物體或物體與背景的交界線。把整個影像所有的交界線勾晝出來,我們就可以對影像作後續的處理。

https://ithelp.ithome.com.tw/upload/images/20171218/20107818RaolZi4fGa.png
圖3)上圖為 經過2階Laplacian微分後的影像
解釋:2階微分處理後,f(x)會產生正負相反的脈波,而像素沒有比0更小的強度(灰階值中:0為黑色;255為白色)。此外,我們在數學公式中得知,脈波是無限的延伸,因此我們需要把超過255的像素,固定為255;把低於0的像素,固定為0。(我會稱作:收歛)(程式碼所處理的影像為RGB,bmp檔)
*一階對比二階而言,後者影像的強度會比前者為大,因此輸出影像的圖像邊界會銳化。(邊界會更明亮)

https://ithelp.ithome.com.tw/upload/images/20171218/20107818HPUpyucRJ5.png
圖4)上圖為 經過2階Laplacian微分,並且經過收歛後的影像

====================分格線====================

void laplacian(unsigned char image_in[Y_SIZE][X_SIZE], unsigned char image_out[Y_SIZE][X_SIZE], double amp, int type){
	int i, j, d;
	int c[3][9] = { 0, -1,  0, -1,  4, -1,  0, -1,  0,
	                   -1, -1, -1, -1,  8, -1, -1, -1, -1,
	                    1, -2,  1, -2,  4, -2,  1, -2,  1 };
	type = type - 1;
	if (type < 0)     type = 0;
	if (type > 2)     type = 2;
	for (i = 1; i < Y_SIZE-1; i++) {
		for (j = 1; j < X_SIZE-1; j++) {
	d = c[type][0] * image_in[i-1][j-1]  + c[type][1] * image_in[i-1][j  ]  + c[type][2] * image_in[i-1][j+1]  
	   + c[type][3] * image_in[i  ][j-1]   + c[type][4] * image_in[i  ][j  ]     + c[type][5] * image_in[i  ][j+1]
	   + c[type][6] * image_in[i+1][j-1]   + c[type][7] * image_in[i+1][j  ] + c[type][8] * image_in[i+1][j+1];
			d = (int)(d * amp) + OFFSET;
			if (d <   0)     d = 0;
			if (d > 255)   d = 255;
			image_out[i][j] = (unsigned char)d;

以上為Laplacian 1至3階微分法的程式碼

=========================>程式解說<=========================

void laplacian(unsigned char image_in[Y_SIZE][X_SIZE], unsigned char image_out[Y_SIZE][X_SIZE], double amp, int type){

image_in[Y_SIZE][X_SIZE] //輸入影像的解析度&像素大小 (矩陣);輸入影像是利用矩陣的方式
image_out[Y_SIZE][X_SIZE] //輸出影像的解析度&像素大小 (矩陣);而輸出影像也是使用矩陣的方式
amp //輸出影像的像素放大率;在低階數或面對較暗沉影像時,我們可以透過放大像素的倍率,而達至影像更明亮的需求。
type //階數微分運算;決定影像需要幾階處理

int c[3][9] = { 0, -1, 0, -1, 4, -1, 0, -1, 0,
               -1, -1, -1, -1, 8, -1, -1, -1, -1,
                1, -2, 1, -2, 4, -2, 1, -2, 1 };

https://ithelp.ithome.com.tw/upload/images/20171219/20107818toBu4h3kCi.png
圖5)上圖為 Laplacian一階微分運算3x3的遮罩
https://ithelp.ithome.com.tw/upload/images/20171219/20107818B8pO9Ptrlx.png
圖6)上圖為 Laplacian二階微分運算3x3的遮罩
https://ithelp.ithome.com.tw/upload/images/20171219/20107818V5kQHvcivy.png
圖7)上圖為 Laplacian三階微分運算3x3的遮罩
*遮罩所產生方式,希望日後有機會詳細說明

type = type - 1;
	if (type < 0)     type = 0;
	if (type > 2)     type = 2;

type = type - 1; // 對應c[type][]的矩陣 矩陣是0~2;人是1~3;所以需要-1,由人的角度轉移至電腦角度
if (type < 0) type = 0; // 如果type小於0,type=0,防呆設計,防止超出範圍
if (type > 2) type = 2; // 如果type大於2,type=2,防呆設計,防止超出範圍

for (i = 1; i < Y_SIZE-1; i++) {
		for (j = 1; j < X_SIZE-1; j++) {

for (j = 1; j < X_SIZE-1; j++) { //掃描輸入的影像,從第一行開始掃描 因為「3x3階數微分運算」是(X-1,Y-1)開始,所以i=1便可以 因為「階數微分運算」是(X+1,Y+1)結束,所以X_SIZE需要-1
for (i = 1; i < Y_SIZE-1; i++) { //掃描輸入的影像,每一行掃描結束後,往下一行再重新掃描 因為「3x3階數微分運算」是(X-1,Y-1)開始,所以i=1便可以 因為「階數微分運算」是(X+1,Y+1)結束,所以X_SIZE需要-1

d=c[type][0]*image_in[i-1][j-1] + c[type][1]*image_in[i-1][j ] + c[type][2]*image_in[i-1][j+1]
+ c[type][3]*image_in[i ][j-1]  + c[type][4]*image_in[i ][j ]  + c[type][5]*image_in[i ][j+1]
+ c[type][6]*image_in[i+1][j-1] + c[type][7]*image_in[i+1][j ] + c[type][8]*image_in[i+1][j+1];

此程式為「3x3階數微分運算的遮罩」對每一個輸入影像陣列作相乘,從而達到微分的效果。

d = (int)(d * amp) + OFFSET;

(int)(d * amp) //其中d變數為int(int為4 bytes) ; amp為double(double為8 bytes)
//d變數 x(乘) 放大倍數後,並轉換成int型態(收歛為整數,減少記憶體使用)
//d變數不會「四捨五入」
d = (int)(d * amp) + OFFSET; //計算結果 x(乘) 放大倍數 + 偏移量
//雖然我們可以用amp把輸出影像的像素以倍數方式放大;但OFFSET可以對像素作微量的調整。

if (d <   0)     d = 0;
if (d > 255)   d = 255;

在上文也提及到,在微化運算所產生的脈波,有機會超出0至255這個範圍,而且上段程式曾把「變數d作放大」,因此需要作出收歛。
if (d < 0) d = 0; //如果計算結果小於0 ,則d=0;
if (d > 255) d = 255; //如果計算結果大於255, 則d=255;
如果是筆者,我會直接修改程式直接用以下方法:

d = (unsigned char)d;

// unsigned char為1 bytes;範圍:0~255
//這樣也可以達到收歛的效果

image_out[i][j] = (unsigned char)d;

經過收歛後,資料型態需要更改(減少記憶體使用量)
(unsigned char)d; //unsigned char = 0~255
//原來d為int (32 bits)是多預留空間作運算
//經過收歛後便可轉換資料型態,減少記憶體使用量
image_out[i][j] = (unsigned char)d; //把新的強度(像素) 放在輸出端(陣列)

=========================>結果<=========================
https://ithelp.ithome.com.tw/upload/images/20171219/20107818b34ZnkJU0G.jpg
圖8)上圖為原始影像 (圖片取自網路)

https://ithelp.ithome.com.tw/upload/images/20171219/20107818hJOtj57cOu.png
圖9) 上圖為 原始影像轉換為bmp檔案,把每一像素強度限制於0-255 (減少檔案佔用空間,但同時也令影像失去美感)

https://ithelp.ithome.com.tw/upload/images/20171219/20107818m5XZ22KpHT.png
圖10) 上圖為 一階微分,放大1倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818sZK6B0bcum.png
圖11) 上圖為 一階微分,放大5倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818TeM8dSsiBb.png
圖12) 上圖為 一階微分,放大20倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818gyAgXdJi0N.png
圖13) 上圖為 一階微分,放大100倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/201078185Vttutfbiv.png
圖14) 上圖為 一階微分,放大2147483647倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818mrynTgSp4l.png
圖15) 上圖為 二階微分,放大1倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818ZEPSydkPm9.png
圖16) 上圖為 二階微分,放大5倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818HxilTurMMi.png
圖17) 上圖為 二階微分,放大20倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818GZ0voHMwOq.png
圖18) 上圖為 二階微分,放大100倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818WOfR1AdXnO.png
圖19) 上圖為 三階微分,放大1倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818PbmOUs6jkQ.png
圖20) 上圖為 三階微分,放大5倍後影像

https://ithelp.ithome.com.tw/upload/images/20171219/20107818xuAj5dJtBm.png
圖21) 上圖為 三階微分,放大20倍後影像

===========================延伸===========================
!~!~!~!~!~!~!~!~!~!~!~!~! 問題一 !~!~!~!~!~!~!~!~!~!~!~!~!
問題: laplacian function中的amp(放大倍數),如果過度放大會影像會變得更暗沉。(見圖14:一階微分,放大2147483647倍後影像)
說明: 因為在運算過程中,每一像素的強度為int(即4個位元組數),如果超出最高位元組數,數值會變為負數,再存放至變數中。(見圖22: 超出資料類型之結果)
在int 32位元中,最高位元(第32位元)是用來控制正負數。

解決辦法1:在運算過程中把int改為long,提升位元組數。
解決辦法2:在運算過程中把int改為unsigned int,防止出現負數。

https://ithelp.ithome.com.tw/upload/images/20171219/20107818ztG96FlkHX.png
圖22) 上圖為 超出資料類型之結果

!~!~!~!~!~!~!~!~!~!~!~!~! 問題二 !~!~!~!~!~!~!~!~!~!~!~!~!
問題: 輸出影像放大倍率超過20後,運算過程只要不超過int的極限,輸出影像的亮度沒大改變。

解決辦法1:使用更高階數的Laplacian微分。在(圖12:一階微分,放大20倍後影像)與(圖17:二階微分,放大20倍後影像)中可以看出,放大倍數不變的情況下,愈高階數微分,在觀看上影像更明亮。
說明: 同時,我們可以從(圖2經過1階Laplacian微分後的影像)及(圖3 經過2階Laplacian微分後的影像)中的數學得知,在2階Laplacian微分後所產生的正負相反脈波,經過收歛後,像素強度雖然有機會與1階Laplacian微分一樣(同為255),但2階Laplacian微分負脈波收歛後,影像的黑白會產生強烈的對比,在視覺上顯得較明亮。

解決辦法2:利用OFFSET變數,為laplacian function產生偏移量,對影像的像素作調整。


下一篇
99乘法表到底可以怎麼玩? [1] \t排版
系列文
提神?看程式比喝咖啡更有效。30

1 則留言

0
微中子
iT邦新手 4 級 ‧ 2017-12-26 13:56:34

所以看起來二階微分反而比較清楚?

對的,二階產生的邊界較分明
而且一階與二階,同樣是利用3x3遮罩方式,所以運算時間是一樣。

我要留言

立即登入留言