2012年7月18日 星期三

Android之JNI

就如我第一篇android說的,我不是很關心android如何應用這樣的機制去寫作應用程式,但是本身卻對機制的原理相當感興趣,如果想要學會如何使用NDK做JNI可以參考參考資料,步驟大概如這張圖

針對JNI在Android上大概分成三層Java/App<=>JNI Lyaer<=>Native
因為有些動作只能在Native完成,所以不得不使用C語言,但是使用者介面又是以Java構成,要使兩者可以溝通就是透過JNI的機制
首先看Java code,必須使用System.loadLibrary載入相對應的share object(.so),對於native function則使用native關鍵字標示出來
 在java內已經宣告了java這邊的介面,用native表明到時候要透過JNI去呼叫Native程式,這時候用javah來編譯出C++ header file,使用JDK工具以特定格式去產生Native function的介面,這樣JNI才會認得兩邊的接口/介面,JNI這位中間人,就是依賴這樣的規則才能為兩邊牽線,下面是javah產生出來的.h檔案
跟著就是自行產生.cpp檔案,實作.h的介面

當呼叫System.loadLibrary的時候JNI會呼叫JNI_OnLoad函數,並且剖析share object(JNIEXPORT關鍵字做的手腳,export某些symbol),並且動態的將這些method註冊到JNI中,java code呼叫某個native method的時候其實是去尋找JNI中註冊表的method,來做呼叫

由於兩種程式語言的基本type並不相同,在兩者之間傳遞資料變成一件很重要的事情,所以如何對應呢?
<type>=>j<type>,如boolean(java)=>jboolean(c++),支援的type有boolean、byte、char、short、int、long、float、double

那麼如何在Native裡面呼叫java的某個物件的某個方法呢?其實如果熟悉Java Reflection這問題會容易解的多。
JNIEnv包含了JNI_OnLoad的時候做的資訊以及JVM的資訊,透過JNIEnv可以找到對應的class,再透過對應的class找出method與field,建構適當的signature就可執行某個object的method,跟Reflection的工作是大同小異的。


另外有個地方要注意的是,預設的狀況在Native並不會增加reference count,也就是說如果再Native functionm用static variable參考到某個object,並不表示下次來,這個object還會存在。可以使用env->NewGlobalRef(some_java_object)來取得global reference,但是要注意,這個global reference會使得garbage collect無法回收該物件,需要使用env->DeleteGlobalRef(some_java_object)來歸還


Native的exception是由JNI回報的,但是C++的異常蠻明顯不大相容於java,且處理很容易讓程式當掉,這時候JNIEnv可能用ExceptionOccured、ExceptionClear、ThrowNew報告異常

參考文件:
http://cheng-min-i-taiwan.blogspot.tw/2011/05/java-native-interface-jni-androidndk.html
http://blog.csdn.net/nicholas6lee/article/details/7324087
http://gyht0808.iteye.com/blog/763435
http://my.unix-center.net/~Simon_fu/?p=833

Learning OpenCV -- chapter 5

這一章談的是圖形的處理
blurring(平滑):用以去除雜訊或者讓影像模糊,基本原理是平均mask/filter內的pixel values
void cvSmooth(const CvArr* scr, CvArr* dst, int smoothtype=CV_GAUSSIAN,
int param1=3, int param2=0, int param3=0, int param4=0);
平滑的type有: CV_BLUR(簡單模糊)、CV_BLUR_NO_SCALE(簡單無縮放模糊)、CV_MEDIAN(中值法)、CV_GUASSIAN(高斯模糊)、CV_BILATERAL(雙邊濾波)
其中簡單與高斯支援in-place,也就是destination或取代source
對於高斯而前param1與param2分別是mask/filter的width與height,第三個參數為sigma,如果要讓xy方向不同的sigma可以使用第四個參數

形態學(Morphology)
Erode(膨脹)與Dilate(侵蝕)是兩個常見的手法,用以將影像的一個部分與kernel(核)做convolution運算,每個kernel有個anchor point(通常位於中心)。膨脹求取局部最大值,使圖形中明亮的區域擴大,侵蝕則是求取區域最小值,侵蝕可以將一些斑點的noise去除
void cvErode(IplImage *src, IplImage *des, IplConvKernel *B=NULL, int iteration=1);
void cvDilate(IplImage *src, IplImage *des, IplConvKernel *B=NULL, int iteration=1);
這兩個運算都支援in-place,若是kernel不指定,則使用3x3的kernel,iteration則表示執行的次數
建立kernel的方式為
IplConvKernel * cvCreateStructuringElementEx(int cols, inr rows, int anchor_x,int anchor_y, int shape, int* values)
望文生義,前面四個參數就不解說了,第五個參數表示形狀有CV_SHAPE_RECT、CV_SHAPE_CROSS、CV_SHAPE_ELLIPSE、CV_SHAPE_CUSTOM
第五個參數表示特定的形狀
在Morphology往往反覆使用上面兩個運算,達到一些特殊目的

void cvMorphologyEx(
   const CvArr*   src,
   CvArr*         dst,
   CvArr*         temp,
   IplConvKernel* element,
   int            operation,
   int            iterations   = 1
);
OpenCV預設支援幾種常見的operator(CV_MOP_OPEN、CV_MOP_CLOSE、CV_MOP_GRADIENT、CV_MOP_TOPHAT、CV_MOP_BLACKHAT)
Open先Dilate再Erode,去除小型的高亮度區域
Close先Erode再Dilate,去除低亮度區域的孤立點
Gradient=dilate(src)-erode(src),邊界
TopHat=src-open(src)
BlackHat=close(src)-src

Flood Fill,就如小畫家的油漆桶一般

void cvFloodFill(
   IplImage*          img,
   CvPoint            seedPoint,
   CvScalar           newVal,
   CvScalar           loDiff    = cvScalarAll(0),
   CvScalar           upDiff    = cvScalarAll(0),
   CvConnectedComp*   comp      = NULL,
   int                flags     = 4,
   CvArr*             mask      = NULL
);

調整大小

void cvResize(
   const CvArr*   src,
   CvArr*         dst,
   int            interpolation = CV_INTER_LINEAR
);
其中調整的時候是如何處理過多或者刪減的pixels,由最後一個參數指定,有CV_INTER_NN (Nearest neighbor)、CV_INTER_LINEAR(Bilinear)、CV_INTER_AREA(Pixel area re-sampling)、CV_INTER_CUBIC(Bicubic interpolation)


Image Pyramids看不懂,orz


取Threshold,運算有

double cvThreshold(
   CvArr*         src,
   CvArr*         dst,
   double         threshold,
   double         max_value,
   int            threshold_type
);


相對應的type為CV_THRESH_BINARY、CV_THRESH_BINARY_INV、CV_THRESH_TRUNC
CV_THRESH_TOZERO_INV、CV_THRESH_TOZERO


取threshold是一種技巧,友人發展出自動化的技巧,透過加權平均的方式自行取得threshold

void cvAdaptiveThreshold(
   CvArr*         src,
   CvArr*         dst,
   double         max_val,
   int            adaptive_method = CV_ADAPTIVE_THRESH_MEAN_C
   int            threshold_type  = CV_THRESH_BINARY,
   int            block_size      = 3,
   double         param1          = 5
);
這個function並不支援in-place

以上的函數都是OpenCV已經幫忙實作完畢,只要直接套用就可以,相當方便,但是重點應該是何時該使用何種運算,以及運算背後代表的意義,才是我們要學習的重點(還是直接去翻閱Digital Image Processing的書XD)



2012年7月17日 星期二

android架構

在Android架構,總共是由5個部份來組成。分別是:
(1)Applications(應用程式)
(2)Application Framework(應用程式架構)
(3)Libraries(函式庫)
(4)Android Runtime(Android執行環境)
(5)Linux Kernel(Linux核心)
大多數的網路資訊引用了類似這樣的圖形
我注意到比較有趣的是Binder (IPC) driver被嵌入了kernel,印象中這是android創建的IPC機制。許多人也知道android上的app開發大多是java,也就是java透過application framework在與底層的JNI以及Native的library做溝通,而我感興趣的就是底層的部分(application framework以下)
為何會對這部分有興致,主要想要操作camera以及最近遇到一個有人release出來的ICS版本,但是SD卡卻無法順利掛載,開始追蹤Vold這個deamon(android藉由uevent實作出來的一個動態掛載volumn的機制),或許就是為了喝牛奶養了一條牛吧XD
最後補上一個camera個架構圖
參考資料:
http://my.opera.com/ozzyyngwie/blog/android-an-open-mobile-platform
http://www.kandroid.org/online-pdk/guide/camera.html

Learning OpenCV -- chapter 3之一

OpenCV的主要四種基本資料結構
CvPoint => CvPoint2D32f, CvPoint3D32f,最前面為兩個整數變數x,y 後者則為浮點數
CvSize => CvSize2D32f,兩個整數變數width, height或者為浮點數的結構
CvRect有成員x, y, width, height
CvScalar,四個double的數值,再不考量記憶體使用量可以用來取代前面三者
通常constructor為cvPoint()或者cvSize,也就是第一個字母為小寫的function,在這裡雖然OpenCV是用c語言寫成,但是其實有不少物件的觀念,所以就借用了constructor一詞,但是並非真的是constructor

圖形的資料結構
CvArr
CvMat
IplImage
如果有個function要求CvArray*,則可以放入CvMat*或者IplImage*,同理要求CvMat*則可放入IplImage*,可以將關係視為繼承,也就是說is-a的關係



CvMat * cvCreateMat(int rows,int cols, int type);
表示這為一個二維陣列,望文生義,rows跟cols表示列跟行,type則是
CV_<bit_depth>(S|U|F)C<number_of_channels>
bit_depth也就是色階,S|U|F表示signed|unsigned|floating-point,C表示channel個數
CV8UC3,表示每個pixels由3個channel構成,每個channel用8bits表示
data則是實際儲存資料的地方
而cvCreateMat實際上呼叫了cvCreateMatHeader()與cvCreateMatData(),前者填充rows/cols/type等等,後者對儲存資料的空間作配置,所以如果不需配置記憶體的時候,或者共享某個區塊的時候,就只要呼叫cvCreateMatHeader就可以了。如果是複製某個區塊,使用cvCloneMat(CvMat*),有配置就必要釋放cvReleaseMat(CvMat*)

存取CvMat資料的方式有用Macro
CvMat *mat=cvCreateMat(5,5,CV_32FC1);
CV_MAT_ELE(*mat,float,3,2);
*((float*)CV_MAT_ELE_PRT(*mat,float,3,2))=5.5
由參數得知,必須要知道data的type才能計算到正確位置,還有就是它是一次性計算,也就是非指標運算,對於連續存取很沒效率

另外一種為cvPtr*D與cvGet*D的方式取得指標,用於各種維度
uchar* cvPtr1D(const CvArr* arr, int idx0, int *type=NULL)
uchar* cvPtr2D(const CvArr* arr, int idx0, int idx1, int *type=NULL)
uchar* cvPtr3D(const CvArr* arr, int idx0, int idx1, int idx2, int *type=NULL)
uchar* cvPtrND(const CvArr* arr, int *idx, int *type=NULL, int create_node=1, unsigned* precalc_hashval=NULL)
或者
double cvGetReal1D(const CvArra *arr, int idx0)
double cvGetReal2D(const CvArra *arr, int idx0, int idx1)
double cvGetReal3D(const CvArra *arr, int idx0, int idx1, int idx2)
double cvGetRealND(const CvArra *arr, int *idx)

CvScalar cvGet1D(const CvArr *arr, int idx0)
CvScalar cvGet2D(const CvArr *arr, int idx0, int idx1)
CvScalar cvGet3D(const CvArr *arr, int idx0, int idx1, int idx2)
CvScalar cvGetND(const CvArr *arr, int *idx)

其中cvPtr*D取得為每個pixel,如果有3個channel,則為rgbrgbrgb...等地排列方式,這部分區自行處理,好處是可以快速地訪問,另外有cvGet*D就有cvSet*D
另外要注意的是CvMat未必是連續空間,最多保證每個row是連續的

從上面可以看到IplImage的成員,大多可以望文生義
重要的有origin,表示原點座標是放置在IPL_ORIGIN_TL左上角(一般電腦圖形)或者IPL_ORIGIN_BL左下角(一般數學座標)
dataOrder表示資料是每個IPL_DATA_ORDER_PIXEL或者IPL_DATA_ORDER_PLANE,前者表示rgbrgbrg這樣的資料排列,後者表示rrrrr...ggggg...bbbbb....這樣三面的排列,所以儲存影像資料的imageData就受到這個參數的影響,必須知道資料正確的排列才能正確處理資料
ROI則是感興趣的範圍,用以將圖形特定範圍切出,用來處理這部分,有mask的觀念,可以使用
void cvSetImageROI(IplImage *img, CvRect rect)
void cvResetImageROI(IplImge *img)
可見一個IplImage只能設定一個ROI,但是要保留多個ROI,書中有範例技巧
參考資料:
http://yester-place.blogspot.tw/2008/07/cvmat.html
http://yester-place.blogspot.tw/2008/07/iplimage1.html

2012年7月16日 星期一

Learning OpenCV -- chapter 2

載入image到記憶體
IplImage *img=cvLoadImage(filepath);
載入avi檔案,並且使用cvQueryFrame取得下一個frame
CvCapture *capture=cvCreateFileCapture(path);
IplImage *frame;
for(...){ frame=cvQueryFrame(caputre);}
調整capture,cvSetCaptureProperty與cvGetCaputreProperty
cvSetCaptureProperty(caputre,CV_CAP_PROP_POS_FRAME,pos);
建立影像,第一個參數為大小,第二個為每個channel的bits數,第三個為通道數目
IplImage *out=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,3);
使用演算法
cvSmooth(img,out,CV_GAUSSIAN,3,3);
建立一半大小的影像
IplImage *out=cvCreateImage(cvSize(img->width/2,img->height/2),img->depth,img->nChannel);
cvPyrDown(img,out);
從上面可以知道,所有重要的資料都被封裝成struct,然後以指標的形式在傳遞

總結以上,OpenCV是一個C lib的library,提供一個快速開發的介面,有效的節省programmer的時間,我想主要的目的是驗證一些演算法吧,真的還蠻方便的

2012年7月15日 星期日

OpenCV 2.4與Visual C++ 2010 Express

主要參考這裡
http://www.cnblogs.com/freedomshe/archive/2012/04/25/2470540.html

大致上就是設定include、設定lib,還要設定linker lib,最後記得把環境變數加進來,讓程式找的到dll,之前裝VC 2008 Express因為忘記設定(其實是找不到orz,好個VS,設定改來改去),結果就一堆symbol找不到的問題

2012年7月14日 星期六

udev的流程

udev主要功能

  • 允許動態建立刪除裝置
  • 自行配置裝置編號
  • 根據規則建立名稱,而非固定名稱
  • 建立kobject物件


udev其實利用了kobject在/sys下面建立的動態的系統資訊,udev主要成分,namedev、libsys、udev

udev配合hotplug機制,每當有裝置加入的時候,建立sysfs(kobject)下的相對應節點,跟著呼叫namedev在/dev下建立相對應的裝置
如果理解bus、device與driver三者的關係,可以理解到udev功能是可以達成的,但是接著升起的疑問是,流程是如何呢?

bus會利用match這個操作方式尋找合適的device以及driver,但是因為需要kobject來在sysfs下建立相對應的結構,是否意味著要改寫driver?從參考資料的解說,應該是不必。udev會監控linux載入的module,自動生成kobject

所以一開始就註冊了bus,udev會在module載入的時候紀錄準備生成kobject,當裝置插入主機的時候,跟著建立device,使用bus尋找到相關driver,最後udev會生成kobject,也就是在/sys下建立相對應的目錄

bus(開機時候)=>driver/udev(載入module時候)=>device(裝置插入主機接上某個bus)=>/dev/xxx與/sys/bus/xxx建立(udev透過bus.match建立)

以上就是我的理解,不知道有沒有錯誤

參考資料:
http://daydreamer.idv.tw/rewrite.php/read-49.html