2009年3月10日 星期二

簡易的兩張圖片處理

上班沒事情做來打打文章=3=

一開始我在宣告時:搞了一個三維的的Array如下

myRGB1[3][320][240]
myRGB2[3][320][240]


想說這樣寫的話在loop上就可以寫的很短

雖然Compiler也過了

但是卻會以Runtime error收場=3=

上網查了一下,發現BCD好像不能宣告太大的陣列(但是明明就很小阿=3=)

不知道以Dynamic allocate的方式能不能宣告到更大的陣列..回家有空再試


上面這張是將兩張圖片做比較,有差異的部份做反白的動作,只是用的演算法非常之爛,雜訊會很多=3=

這張就只是單純的相加/2而已

試驗過程中有個小問題就是市面上的Webcam都有自動補光的功能...
只要他測光的點光源有變化,它就會改變整張圖的亮度
這樣的話要在圖片上面做判斷就危險
不知道有沒有Function可以把它關掉的
回去再找找

2009年3月5日 星期四

我對 Scanline

一點點看法 看過就好

首先要說明的是,在講義 1.2.2 儲存影像陣列裡的
第一部分程式碼 (page.14)

 TCanvas *csBuf=new TCanvas;
 HDC hDC=GetDC(Panel->Handle);
 csBuf->Handle=hDC;

 Graphics::TBitmap*Source=new Graphics::TBitmap();

 Source->Width=Panel->Width;

 Source->Height=Panel->Height;

 Source->Canvas->CopyRect(Rect(0, 0, Source->Width, Source->Height),

              csBuf,
              Rect(0, 0, Panel->Width, Panel->Height));
 Image->Picture->Bitmap->Assign(Source);


 for(i=0;i<BitmapInfoHead.biHeight;i++)
 {
  ptr=(Byte *) Image->Picture->Bitmap->ScanLine[i];
  for(k=0,j=0;j<BitmapInfoHead.biWidth;j++)
  {
   background[i][j][0] =ptr[k+2];
   background[i][j][1]= ptr[k+1];
   background[i][j][2] =ptr[k];
   k+=3;
  }
 }
只要key in藍色部分,就會顯示出來一張圖了!!
顯示圖片靠 Image->Picture->Bitmap->Assign(Source);裡的Assign( )函數
(我覺得這範例寫得不好,傳回給source會影響截圖計算,
可能會給初學者錯誤的觀點,所以我改成background

而且範例需要修改正確 ... 講義上都是不完整的code)

至於使用Scanline的部分正如敘述所說,是將像素值輸入到陣列中
是給後續使用的,跟顯示影像無關
如果你只是想顯示一張擷取的影像,請不要理後續Scanline的部分

這樣瞭解了吧?
anyway, 下面接著講 Scanline


=========================================

 ptr=(Byte*) Image->Picture->Bitmap->ScanLine[i];


就是這條混蛋指令讓人死一堆腦細胞
ptr是什麼? 為什麼可以這樣做?? Scanline的功能???

首先,對於這條指令,還必須得宣告 Byte *ptr;才能使用
也就是如下:
 Byte *ptr;
 ptr=(Byte *) Image->Picture->Bitmap->ScanLine[i];

ptr是個Byte型態的變數,加個星號就是指標嘛
所以 *ptr儲存的是Byte型態的記憶體位址

(Byte *)Image->Picture->Bitmap->ScanLine[i];
是該Image,第 i行(height)的起始位置


以此3x3的圖為例,
 (Byte*)Image->Picture->Bitmap->ScanLine[0]; 回傳的就是0xA0
 (Byte*)Image->Picture->Bitmap->ScanLine[1]; 回傳的就是0xB0
 (Byte*)Image->Picture->Bitmap->ScanLine[2]; 回傳的就是0xC0

用很白話的講,Scanline就是掃瞄一條線,

他指向某條水平線第一個點的位子

如果你要問我為什麼要加
(Byte*) ?
別傻了,單純用Image->Picture->Bitmap->ScanLine[i];
跑出來的應該是RGB的值吧
我猜的

想多認識(Byte*)可以參考此網誌
http://www.wretch.cc/blog/hhmmjj/9594208




這樣應該懂Scanline是做什麼的了吧?
所以,
 for(i=0;i<BitmapInfoHead.biHeight;i++)
 {
  ptr=(Byte *) Image->Picture->Bitmap->ScanLine[i];
  for(k=0,j=0;j<BitmapInfoHead.biWidth;j++)
  {
   background[i][j][0] =ptr[k+2];
   background[i][j][1]= ptr[k+1];
   background[i][j][2] =ptr[k];
   k+=3;
  }
 }
這段,其實就是將Image裡每個pixel的RGB值傳給background
讓你可以將background的值累加起來後取平均,
然後把background顯示出來,就可以得到視訊的背景圖了



再囉嗦一點

上一篇文章作者提到因不懂Scanline
所以改用GetRValue()、GetGValue()、GetBValue()
去擷取Image的RGB值

這兩者有什麼差異?
最大的差異就是在執行速度吧

上禮拜在作業系統的課上老師有提到,
以前ANSI C用來寫OS的武器之一就是指標 => *

用指標直接存取記憶體的速度本來就較快,
更何況用了三個函數才能去得RGB值

假設
ptr[k]    取出R值的速度是0.01秒
GetRValue() 取出R值的速度是0.1秒
相差10倍

每一個pixel有RGB三個值,等於需要花 0.03 : 0.3
一張800x600的圖片有480000個pixel
這樣用函數取RGB所花的時間會比用指標多花上10x480000倍 ...
應該是這樣算吧,我只是舉例有錯不能怪我



還有一張圖畫了沒用到,不管,我還是要放上來



p.s.
在blogspot可以利用修改css,讓文章內可以有個物件包住程式碼
  看起來也比較美觀 ...
  可以參考這篇文章修改:【Blog】文章中引用程式碼的作法

=================================================

附錄: i++ v.s. ++i

老師說這個不會也不影響寫程式是沒錯
但是多瞭解一些還是可以幫助釐清觀念
尤其是上arping的課如果有要寫程式時,沒分清楚可能就哉了
雖然我也沒很懂,簡易的描述使用上的差異應該還是ok的

把寫好的程式進行編譯時,
編譯器也是從上到下,由左至右讀取文字判斷要編譯成什麼樣的低階語言
所以
 i++; 編譯器先讀到 i 再讀到++,所以把 i 加1
 ++i; 編譯器先讀到++,再讀到 i ,接著把 i 加1
看起來意思都相同,但這是因為這兩句語法都是獨立使用

如果程式碼長這樣呢?
 int a[3]={0,0,0};
 i=0;
 a[i++]=1;
 i=0;
 a[++i]=1;
看起來似乎都是把a[1]的值變成1,輸出就會是 {0,1,0} 對吧?
其實執行結果陣列a的內容會是這樣 => {1,1,0}

 i=0;
 a[i++]=1;
編譯器會先把 i 提出來,並把 i 的值,0給a[]使用
然後才做 i++
所以這裡其實是 a[0]=1; i 的值為1

 i=0;
 a[++i]=1;
編譯器會先執行 ++ 的動作,也就是把 i 加1之後,
才把 i 的值,1給a[]使用
也就是這裡其實是 a[1]=1; i 的值為1

所以真正的執行結果陣列a的內容會是 {1,1,0}

就我的印象,arping好像還講過 i++ 和 ++i 在執行上還有別的差異
那時他沒多講我也沒多問就 .... 現在就不會了


 i=0;
 a[(i++)]=2;
你覺得陣列a之中哪個位置會變成2?

 i=1;
 c = (i++)+(++i)+(i++)+(++i)+i+(i++)+(++i);
最後c的值會是多少?

我用手算是29,用Dev-C++執行結果卻是19
怎麼會這樣 ... ??? (使用不同的IDE可能會有不同的執行結果)

試試看吧 很好玩的 :D

2009年3月3日 星期二

靜態影像擷取~

(如看不清楚,請自行點開放大看.)

情況一: 攝影機拍攝內容不變, 連續拍攝 A 與 B, 計算其平均差異。
(A+B)/2:


(B-A)/2:

(注:Button 1 即 按鈕A ,以此類推.)

畫面分別為~
左上:視訊擷取畫面.
右上:儲存視訊的靜態影像A.
左下:儲存視訊的靜態影像B.
右下:A&B靜態影像的平均差異值的影像C.

情況二: 攝影機拍攝 A 後, 放置一物體到攝影機前, 在按 B 取得影像陣列 B 內容, 再計算其平均差異。
(A+B)/2:


(B-A)/2:



雖然之前修過老師的課,
但是因為隔得有點久了,
其實也忘得差不多了.
所以其實花比較久的時間,
在回憶和看以前曾寫過的程式.

起先~
我一直搞不清楚,使用到ScanLine[ ]時,
到底是什麼時候就讀取data的,
又是用了什麼將單張影像顯現的. (現在還是沒搞懂...)

上課的時候,
我的進度一直只到把A&B兩按鈕的功用做出來而已.
再來就一直無法前進.

但是課堂上老師說他並沒有使用到ScanLine[ ],
所以回家繼續寫的我,
就去找了有相當功用的程式.
最後我選擇用GetRValue(),GetGValue(),GetBValue(),
去擷取Image的RGB值,
然後將擷取的data以RGB值顯現.

因為以前寫的程式,有RUN出來.
原本以為會很快就能改出來,
沒想到居然RUN了好幾次都沒反應.

如果有偵錯出來,那我還能debag.
但是難就難在,什麼錯誤訊息都沒跳出來.
所以我又耗了一下.

最後,
也不知怎麼了,
我把for loop範圍的
BitmapInfoHead.biHeight和BitmapInfoHead.biWidth
改成一般數值.

居然就能顯現了~
真是可喜可賀啊!!~ (啥鬼)

在情況一時~

因為攝影機拍攝內容不變,
所以兩張圖的差異並不大.
(雖然有時候手可能會稍微抖一下啦~
或是光可能也會有點影響.)
但是基本上,還是一張看不太出來差別的圖~ (相加除以二的情況)

若是相減除以二的情況,
則是一張偏黑的圖.

情況二時~~

相加除以二的情況,
則有浮水印般的透明效果.

若是相減除以二的情況,
因為有放了一個物體到畫面中,
所以畫面的差異度變高了.

但是~
由圖可以發現,
在A畫面是黑色或深色的區塊,

若物體剛好擋到的話,
就能看到較清楚的原物體的樣子.
(ex:圖中的瓶子和橡皮擦)

不過比起原來的顏色,
應該會更暗些.