畢業論文-基于Kinect的人體建模中點云配準研究_第1頁
畢業論文-基于Kinect的人體建模中點云配準研究_第2頁
畢業論文-基于Kinect的人體建模中點云配準研究_第3頁
畢業論文-基于Kinect的人體建模中點云配準研究_第4頁
畢業論文-基于Kinect的人體建模中點云配準研究_第5頁
已閱讀5頁,還剩55頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

本科畢業論文畢業論文(設計)論文(設計)題目:基于Kinect的人體建模中點云配準研究姓名學號學院山東大學軟件學院專業數字媒體技術年級指導教師2016年5月18日

目錄摘要 1ABSTRACT 2第1章緒論 31.1引言 31.2三維人體測量技術 31.2.1非接觸式人體測量技術 41.2.2國內外研究背景 41.3Kinect簡介 6第2章系統環境的調配與數據的獲取 92.1系統環境的調配 92.1.1配置KinectSDK 92.1.2配置OpenCV及OpenNI2 112.2數據的獲取及簡單處理 122.2.1深度數據的獲取 122.2.2點云數據的獲取 15第3章骨骼數據的處理 203.1骨骼數據的獲取 203.2骨骼數據的校準 243.2.1kd-tree算法簡介 253.2.2利用Kd-tree查找最鄰近點 28第4章基于骨架的點云配準 344.1基于骨架的動作變換 344.1.1動作變換算法 344.4.2對變換后點云的優化 394.2另一種實現方式 40第5章總結與展望 425.1全文總結 425.2工作展望 43謝辭 45參考文獻 46附錄1外文文獻原文 48附錄2外文文獻譯文 49

基于Kinect的人體建模中點云配準研究摘要隨著現代三維建模技術的發展,人體建模也成為了一個非常重要的課題。運用MAYA、3DMAX等軟件進行直接建模需要耗費大量的時間以及精力,并且針對不同體型及動作的人體也很難做到靈活的變換。因此,運用人體感知設備進行量化建模可以大大方便人體建模的過程。本文使用MicrosoftKinect可以獲取空間深度信息的功能,對真實世界人體模型的重建進行了測量與研究。首先,通過MicrosoftKinectSDK以及OpenNI提供的系統環境,利用Kinect設備對空間中的人體的深度圖像、深度數據、骨骼信息進行簡單的識別以及提取。然后將經過優化的人體深度圖像與獲取的深度數據結合并形成人體的點云數據。接著,使用一個已經提取了骨骼并基于骨骼進行了分塊的標準人體模型的點云,將其與kinect所獲取的骨架信息匹配,在Matlab中計算出每一塊點云的旋轉矩陣以進行動作的修正。最后,利用四元數法計算出兩個骨架的變換矩陣,形成一個最大程度逼近所獲取信息的人體模型。關鍵字:;Kinect;人體建模;點云;骨骼配準;Matlab

ABSTRACTWiththedevelopmentofmodernthree-dimensionalmodelingtechnology,thehumanmodelinghasbecomeaveryimportanttopic.CarryingonthedirectmodelingbyusingMAYA,3DMAXandothersoftwarerequiresalotoftimeandenergy,inaddition,it'sdifficulttoachieveflexibletransformationfordifferentshapeandmovementofthehumanbody.Therefore,usingthehumanbodysensorstoconductquantizationmodelingcangreatlyfacilitatethemodelingprocessofhumanbody.Inthispaper,usingMicrosoftKinectcanobtainthefunctionofthedeepeninformationinthespace,thustocarryonthesurveyandresearchonthereconstructionofthehumanbodymodelintherealworld.Firstofall,throughthesystemenvironmentprovidedbyMicrosoftKinectSDKandOpenNI,usetheKinectequipmenttocarryonrecognitionandextractiontothedepthimage,depthdata,andbonesinformationofhumanbody.Then,combinethebodydepthimagethathasbeenoptimizedwiththeobtainedthedepthdatatoformthepointclouddataofhumanbody.Afterwards,matchitwiththebonesinformationobtainedfromkinectbyusingapointcloudofastandardbodymodelingthathasbeenextractedtheboneandhasbeenseparatedbasedonthebones,thencalculatetherotationmatrixofeachpieceofpointcloudtoconductthemotioncorrection.Finally,usethequaternionmethodtocalculatethetransformationmatrixofthetwoskeletons,thustoformamodelofthehumanbodythatissimilartotheobtainedapproximationtothemaximum.Keyword:Kinect;PointCloud;Humanmodeling;SkeletonCalibration;Matlab

第1章緒論1.1引言隨著社會的發展,越來越多的行業需要運用到三維人體模型重建技術,如制衣業、動漫業、醫藥業等?,F在的建立人體三位模型一般有兩種方法:一種是通過Maya、3DMax等三維軟件進行構建,這種方法是建立在標準的人體尺寸的基礎上的,所以有很大的局限性;另一種是通過三維掃描技術,通過采集不同視角的人體點云三維坐標進行建?!,F階段比較成熟的非接觸式的測量方法有基于激光掃描儀來實現的,也有通過紅外線和結構光,以現代光學為基礎,輔以電子學、計算機圖像學、信息處理、計算機視覺等科學技術,其精度和測量速度有優于傳統的手工測量方法。但是在現實中,他們都無法被廣泛應用,究其原因主要是進口的非接觸式三維人體測量設備體積過大、價格不菲,小型的商家難以成本低地運輸購買。同時,傳統的三維人體測量設備獲取人體表面特征數據的過程十分復雜。所以,易使用、精度高、成本小的三維人體測量工具有很廣闊的前景。本文完成的問題是運用微軟開發的Kinect體感工具獲取的數據進行快速準確的人體模型重建。1.2三維人體測量技術人體測量師通過測量人體各個部位的尺寸來去頂個體和群體之間在人體尺寸上的區別,以此來研究人的形態特征,繼而為工業設計、人機工程、工程設計、人類學研究、醫學等提供人體基礎資料。隨著時代發展和社會進步,人體數據測量也同樣在不斷的發展和更新。為了對人體體型有一個正確而又客觀的認識,除了做定性研究外,還必須把人體各部委的體型特征數字化,用精確的數據表示人體各部位的特征。三維人體測量作為現代圖像測量技術的一個分支,是一個以光學為基礎,融合光電子學、計算機圖像學、信息處理和計算機視覺等科學技術為一體的測量技術。三維人體測量技術與傳統的手工人體測量技術相比較,最主要的特點是快速、準確、效率高。其能通過快速的人體掃描和數據分析從而準確得出一系列的尺寸,減少了誤差,同時,對傳統方法無法測量的人體形態、曲線特征等也可以進行準確的測量。此外,測量結果還可以通過計算機直接輸送到紙樣設計和自動裁剪系統,完成人體測量、紙樣設計和排料裁剪的連續自動化。因此,在資料完整性和再利用性方面,三維人體測量技術明顯優于傳統的測量方式?,F在的三維人體測量主要是非接觸式人體測量。1.2.1非接觸式人體測量技術非接觸式三維人體測量技術(interactive3Dwholebodyscannersystem)人體全身掃描技術,是通過應用光敏設備捕捉設備投射到人體表面的光(激光、白光及紅外線)在人體上形成的圖像,展現人體三維特征。非接觸式三維自動人體測量彌補了傳統的接觸式人體測量的不足,是得測量的結果更加的可靠、準確。1.2.2國內外研究背景非接觸式三維自動測量師現代化人體測量技術的主要特征,對三維人體測量技術的研究,美國、英國、德國和日本等發達國家開始的較早,大致在70年代中期開始,已研制開發了一系列三維人體測量系統。目前主要是CyberwareWB4、Vitronic、TC、TecMath、Telmat、Hamano以及Hamamatsu等公司產品:CyberwareWB4人體掃描儀是世界上最早的掃描儀系統之一。這個系統是用多個激光測距儀(由激光和CCD攝像儀組成)對站立在測量想內的被測者從多個方位進行測量。攝像機接受激光光束射到人體表面的反射光,通過受光位置、時間間隔、光軸角度,與測距儀同步移動時,可通過計算機算出人體同一高度若干點的坐標,從而可以測得人體表面的全部數據。它產生人體外表面的高分辨率的數據集。這個系統有四個掃描頭被安裝在一個架子上,兩個引擎使得他上下移動,如圖1-1是CyberwareWB4的一個示例。圖1-1CyberwareWB4系統示例TecMath的核心產品是數字化人體模型‘Ramsis’,他是用關節連接的人體模型,能夠與CAD模型相連來訪問工作空間和可見性等。TecMath研制了一臺人體掃描儀以此來講模型的人體測量背景最佳化。其將激光條投射到人體上來檢索人體的外形,雖熱人體的邊緣并不能用這種掃面方法來體現,但是輸入數據充分將Ramsis模型最佳化。圖1-2是這個掃描儀及掃描例子。由于一條制定的告訴現行圖像照相機,對于一個以激光為基礎的系統來說掃描id速度是非??斓?,掃描時間少于2s。圖1-2TecMath系統示例TC2在結構化光柵投射技術的基礎上研制了人體掃描儀。這個測量方法通過白光分層輪廓,取得人體全身的三維數據。其發射二位的光柵,投射在人體的身體上;光柵式樣的強度在水平方向上以正弦曲線的形式變化,在垂直方向是沒有變化的。正弦曲線式樣的相數載四步中轉變,每步為90°角度,生成場景的四個圖像,使用這些圖像,就可以決定每個像素的相數。系統使用粗糙的和細的光柵式樣,粗糙的光柵用來測量不明確的表面,而這些表面用細的光柵測量會產生不連續性。法國Lectra的核心產品是BodyScanner。BodyScanner是一臺3D人體掃描儀。人體從頭到腳掃描一次需要的時間僅需要8s,系統捕獲人體形象后,通過電腦產生一個高度精確的三維圖像。在我國,廣東賽博服裝科研中心投入2400多萬元人民幣啟動了“中國三維熱體數據庫”的項目,成為了國內規模最大的。設備最先進的、專業技術水平最高的服裝研究實體,測量結果使原來的四個號型增加到七個,為今后服裝生產的運作模式——大規模定制和個性定制,實現企業量身定制系統打下了堅實的基礎。1.3Kinect簡介上述的人體測量儀器非常昂貴,又難以保養,不易運輸,對于一些小型的企業購置這樣的一臺儀器可能難以負擔。因此,本文使用了Microsoft(微軟)公司2010年6月發布的XBOX360體感周邊外設Kinect,相較于上述儀器來說,雖然精度沒有大型人體掃描儀高,也不能直接獲取人體的三維模型,但用于一些不十分依賴精確程度的行業,已經綽綽有余。并且,對器獲取的數據進行校準整理后,同樣能得到十分逼近與真實人體的模型。此外,目前此款Kinect售價不到1000人民幣,方便攜帶,給快速人體建模帶來了福音。直至今日,Microsoft公司已經發布了第二代的Kinect,其總體性能大幅優于第一代的設備。本次研究使用了一代Kinect,其外觀與結構如圖1-3所示。對于二代的Kinect,將其系統環境調配好,也能完美支持。圖1-3Kinect一代外觀與結構如圖1-3所示,從外觀上來看,正面有三個攝像頭以及一個指示燈,指示燈負責表示設備是否在運行,運行時我們可以看到指示燈的閃爍。另外,三個攝像頭中,從左往右看,最左邊的是紅外光源,其次是彩色攝像頭,可以獲取前方景色的RGB數據,相當于一臺攝像機,最右邊的是紅外攝像頭,用于接收紅外光源,以獲取空間中物體的深度信息。官方給出的參數稱RGB攝像頭最大能獲取1280*960的圖像,而紅外攝像頭可獲取最大640*480的深度圖像。此外,在感應器的下面有一排麥克風陣列,排列著四個麥克風,并且基座與感應器之間的樞紐是可以通過程序控制轉動的。那么,Kinect是如何獲取空間中的深度數據的呢,就如上面所說,依靠著紅外光源以及紅外攝像頭,他們的發射以及感應的范圍是互為重疊的(如圖1-4)。經由發射、捕捉、計算、重構的過程將它能感知的空間中的每一個點轉化為深度信息,將他們拼合起來組成一幅完整的深度圖像,并且傳感器可以以每秒30幀的速率進行刷新,做到實時快速重建深度圖像。圖1-4Kinect紅外攝像頭視角MicrosoftKinect運用的深度測量技術稱為LightCoding,顧名思義,就是通過光源的發射以及接受感知周圍空間,并進行編碼,這是PrimeSense提出的一種結構光技術。LightCoding技術將其光源稱作“激光散斑”,是激光照射到粗糙物體或者穿透毛玻璃之后隨機形成的衍射斑點。顯然,這些衍射的斑點具有極高的變化性以及隨機性,并且若是距離不同,其圖案也會相應地進行變化。因此我們不難發現,對空間中的任意兩處進行散斑圖的獲取,其結果必然是不同的。因此,對于空間中的物體,只要識別了其所在位置的散斑圖案,便可確認他的具體方位了。所以,如果事先記錄下了這個光源的感知范圍的所有的散斑圖案,空間中的3D再現就不難實現了。這就需要完成一次光源標定,如圖1-5所示。圖1-5結構光測距原理示意圖

第2章系統環境的調配與數據的獲取2.1系統環境的調配為了獲取研究要用的各種數據,首先需要為Kinect搭建系統環境,本文選擇了MicrosoftKinectSDKv1.8這個微軟公司自己提供的SDK進行對Kinect的控制操作。此外,由于官方SDK的一些缺陷,我們還使用了OpenNI2提供的API獲取了某些數據。在圖像的獲取、顯示以及處理方面,我們使用了常用的OpenCVv2.4.9以輔佐開發。2.1.1配置KinectSDK首先,本文使用的儀器為KinectforXBOX360,不能直接與電腦連接,需要購置一個XBOX360厚機體感火牛(如圖2-1所示)接上電源方可連接電腦。圖2-1XBOX厚機體感火牛接著,便可在官網下載KinectforWindowsSDKv1.8(包含兩部分KinectStudio和Developer

Toolkit)下載地址:/en-us/kinectforwindows/develop/downloads-docs.aspxKinect的安裝地址是無法修改的,默認下載到C:\ProgramFiles\MicrosoftSDKs\Kinect路徑中,注意安裝SDK之前,需要斷開Kinect與電腦的連接,并關閉VisualStudio。安裝過程很簡單,等SDK安裝完成之后,將Kinect電源線插上并連接到電腦上,Windows會自動尋找和安裝驅動,安裝完成后就可以識別Kinect,這是Kinect上面LED指示燈會變成綠色。這時候我們就可以打開安裝目錄下的DeveloperToolkitBrowserv1.8.0文件,發現里面有很多已經完成的開發包,點擊其中一個C++文件并運行,如圖2-2,顯然,程序能正常運行了,就表示Kinect順利連接上了電腦。圖2-2KinectSDK自帶程序調試接著,我們就可以打開VS2012配置開發的環境,即include和lib路徑。進入VS,首先創建一個空的win32控制臺應用程序。然后進入項目屬性,在ProjectsandSolutions中,選擇VC++Directories。將Includefiles中加入C:\ProgramFiles\MicrosoftSDKs\Kinect\v1.8\inc;再將Libraryfiles中加入C:\ProgramFiles\MicrosoftSDKs\Kinect\v1.8\lib\x86;最后還需要在鏈接器的輸入中,增加附加依賴性:Kinect10.lib。到此VS的開發環境就配置好了,需要調用Kinect的時候在程序中添加所需的頭文件(如Nuiapi.h)便可使用相關的API了。2.1.2配置OpenCV及OpenNI2首先,進入OpenCV的官方網站/找到合適版本的OpenCV進行下載,本文使用了較普遍的2.4.9版本。然后,我們需要配置系統的環境變量。右擊桌面上的“計算機”然后點擊“屬性”,在出現的界面中點擊“高級系統設置”,選擇“環境變量”,在用戶變量中單機“新建”按鈕,在變量名里輸入:OPENCV,變量值里輸入OPENCV的安裝地址,本文的地址為D:\opencv\opencv\build,最后單擊確定按鈕。然后,在系統變量中找到“Path”變量,雙擊并在變量值的末尾添加:%OPENCV%\x86\vc11\bin。注意,vc10,vc11,vc12分別表示VS2010,VS2012,VS2013的VisualStudio使用的編譯器版本,根據自己的VS版本來填寫正確的編譯器版本號,本文使用了VS2012版本。接著,與配置Kinect一樣,我們在新建的C++項目中,將Includefiles中加入D:\opencv\opencv\build\include;再將Libraryfiles中加入D:\opencv\opencv\build\x86\vc12\lib;最后還需要在鏈接器的輸入中,增加附加依賴性:opencv_calib3d249d.lib;opencv_contrib249d.lib;opencv_core249d.lib;opencv_features2d249d.lib;opencv_flann249d.lib;opencv_gpu249d.lib;opencv_highgui249d.lib;opencv_imgproc249d.lib;opencv_legacy249d.lib;opencv_ml249d.lib;opencv_nonfree249d.lib;opencv_objdetect249d.lib;opencv_ocl249d.lib;opencv_photo249d.lib;opencv_stitching249d.lib;opencv_superres249d.lib;opencv_ts249d.lib;opencv_video249d.lib;opencv_videostab249d.lib等文件。有了上面的環境配置的經驗,OpenNI2便很容易配置了,下載了相應的開發包后,配置好VS中的include和lib路徑,增加附加依賴屬性openNI.lib就可以了。需要注意的是,要從OpenNI'sredist目錄中復制所有文件到你的工作目錄(默認為C:\ProgramFiles\OpenNI2\Redist或者C:\ProgramFiles(x86)\OpenNI2\Redist)。否則程序可能無法運行。2.2數據的獲取及簡單處理配置好系統環境之后,就要開始進行數據的獲取了,本次研究需要的數據有:人體的深度信息、骨架信息、點云數據。骨骼信息獲取之后需要進行相應的處理,相對復雜,將放到第三章詳細描述,本節介紹使用Kinect獲取深度數據以及點云數據。2.2.1深度數據的獲取從1.3節我們可以得知,Kinect可以獲取空間中的深度圖像,分辨率最大為640*480。由此,我們可以利用所安裝的官方開發包進行深度圖像的獲取,然后用opencv繪制出來。學習了KinectSDK可以知道,其中每一個類型的數據中都包含了三個類:Reader,Source和Frame與之對應。現在我們要獲取的是深度數據,因此我們需要研究的便是IDepthFrameSource,IDepthFrameReader,IDepthFrame這樣命名的,對于其他數據,如骨架、彩色、手勢信息,其命名方式也是一樣的。如圖2-3所示,這三個接口是一環扣一環從Kinect中讀取數據的。圖2-3深度數據提取流程首先,我們初始化并且打開了Kinect之后,利用KinectSDK,我們可以向設備請求打開一個源,從當中獲取我們想要的數據,這就是Source接口的用途,其代碼如下:m_pKinectSensor->get_DepthFrameSource(&pDepthFrameSource);其中m_pKinectSensor是Kinect的總端口。然后,通過Source端口我們僅僅是獲取了Kinect端的權限,我們的電腦并沒有模塊去接收信息,因此,我們就需要使用Reader接口在電腦端穿件一個讀取端口,與之前使用Source獲取的信息源綁定在一起。在這之后,當我們想讀取信息的時候,都可以從這個Reader的端口來獲得,其代碼為:pDepthFrameSource->OpenReader(&m_pDepthFrameReader);到最后,Frame才是真的存儲數據的類,他其中的數據源源不斷來自于Reader中,我們需要得到各種各樣的數據的時候,都是從這個類中提取的。列如我們現在要提取的是深度信息,查看KinectSDK的說明我們可以知道,其深度空間的范圍是424*512(高*寬)。提取深度數據的時候,我們就可以創立一個相同大小的數組,其代碼為:m_DepthFrameReader->AcquireLatestFrame(&pDepthFrame);pBodyIndexFrame->CopyFrameDataToArray(cDepthHeight*cDepthWidth,bodyIndexArray);其中代碼第一行是創立一個Frame的類,第二行的cDepthHeight為424,cDepthWidth為512,如此便可存儲深度數據并進行輸出了。我們使用OpenCV生成一個分辨率為640*480的窗口(為了之后數據的校準,我們統一使用640*480的分辨率),然后將數組中的數據成比例地輸出,便可獲取我們測量區域的深度圖以及深度值了。代碼如下:MatdepthImage;depthImage.create(480,640,CV_8UC3);HANDLEdepthEvent=CreateEvent(NULL,TRUE,FALSE,NULL);HANDLEdepthStreamHandle=NULL;namedWindow("depthImage",CV_WINDOW_AUTOSIZE);while(1){If(WaitForSingleObject(colorEvent,0)==0)getColorImage(colorEvent,colorStreamHandle,colorImage);if(WaitForSingleObject(depthEvent,0)==0)getDepthImage(depthEvent,depthStreamHandle,depthImage);imshow("colorImage",colorImage);imshow("depthImage",depthImage);intc=cvWaitKey(1);if(c==27)break; if(c=='s') { std::ofstreamoutfile1; outfile1.open("F://Depth.txt");imwrite("F://cimage.bmp",colorImage); imwrite("F://dimage.bmp",depthImage); for(inty=0;y<480;y++) { constUSHORT*p_depthTmp=depthImage.ptr<USHORT>(y); for(intx=0;x<640;x++) { USHORTdepthValue=p_depthTmp[x*3/2]; SHORTrealDepth =NuiDepthPixelToDepth(depthValue); outfile1<<realDepth<<""; } outfile1<<'\n'; } ImgNum++; outfile1.close(); }}代碼中省略了對Kinect狀態的調用以及判斷語句,通過這個代碼,我們便可獲得原始的深度數據以及深度圖像,如圖2-4所示,深度值的單位是毫米:圖2-4原始深度圖像以及深度值2.2.2點云數據的獲取不難看出,所獲取的深度圖像有一定程度的粗糙,并且我們若要獲取點云的數據,所需要的只有人體這一部分,背景都需要省略。因此,在生成點云數據之前,必須對深度數據做一下優化。由于本次研究的重點不在人體的提取以及深度圖像的優化上,因此我們制作了一些簡單而又方便的處理。由于KinectSDK的智能性,它可以自己識別人體(或某一些物體),將其從背景環境里提取出來,這個性能大大方便了我們的提取工作,下面簡單介紹一下其提取的原理。首先,Kinect會檢測距離機體比較近的區域范圍,因為這個范圍中很有可能會出現所需要的目標物體。接著,系統會在這些區域范圍內進行全面的像素掃描,以判斷這些區域分別屬于人體中的哪些部位。當然,這些過程中運用到了很多計算機圖形學以及視覺的技術,包括邊緣檢測、噪聲閾值處理、對人體目標特征點的“分類”等環節。通過這一技術最終將人體從背景環境中區分出來。其中,邊緣的檢測就是找出圖像中某些要素的突變或者不連貫。通常我們肉眼是很容易識別物體的邊緣,因為我們的大腦可以將每一個物體都獨立開來,而對于計算機,可以對圖像的顏色或者灰度的曲線做評估,并且能夠識別一些簡單的紋理結構的變化。如果加入了深度數據,還可以分層進行檢索,以區分每一個物體。另外,特征點的提取主要是對目標物體進行一些處理如降維操作,對其生成的特征向量、特征點、特征角、特征線段等等進行比較以篩選。對于Kinect,它可以同一時間最多識別6個物體,對其進行被動的追蹤。并且,如果有人體的存在的話,最多可以顯示其中兩個人體的全身骨骼點。進行這個步驟的過程中,Kinect會自動將這些可以識別的物體進行編碼,將每一個可識別物體分割開來。因此,對于雜亂的背景畫面,可以通過這種方式簡單的隔離開來。之后,只需要輸送用戶希望得到的數據,這不僅使得數據更加直觀,而且可以大大減少數據的傳輸以及計算量,減輕電腦的工作壓力。這一技術在其作為游戲的體感工具使用時十分有效,因為游戲的生產商只需要識別人體的部分。由此,我們找到系統中存儲每一個可以識別物體的緩存,定義一個數組,將其存儲進去,最后輸出時,只顯示搜索到的第一個物體便可,其余的像素都設為0。核心代碼如下,在switch語句后面選擇要現實的對象便可:if(LockedRect.Pitch!=0){for(inti=0;i<depthImage.rows;i++){uchar*ptr=depthImage.ptr<uchar>(i);uchar*pBuffer=(uchar*)(LockedRect.pBits)+i*LockedRect.Pitch;USHORT*pBufferRun=(USHORT*)pBuffer;for(intj=0;j<depthImage.cols;j++){intplayer=pBufferRun[j]&7;intdata=(pBufferRun[j]&0xfff8)>>3;ucharimageData=255-(uchar)(256*data/0x0fff);switch(player)這樣我們便提取了對象(如圖2-5(1)所示),之后對深度圖像做一下簡單的優化,本文選擇了聯合雙邊濾波的方法進行去噪,優化后的深度圖像如圖2-5(2)所示。(1)(2)圖2-5優化過的深度圖像接下來,我們基于提取并優化后的深度圖像,將有灰度值顯示的區域的人體信息以點云的形式顯示出來,由于KinectSDK中存在著一定的缺陷,只能獲取深度值而不能獲取那一點對應于真實是世界的x與y的值。一種方法是通過三角的計算,將每一個點的x和y值基于像素的位置計算出來,這種方法的缺點就是計算量龐大,計算公式復雜,容易出錯。于是,我們便尋找了另一種系統環境來獲取點云數據——OpenNI2。在OpenNI2的環境下,有一個convertDepthToWorld函數,這個函數可以直接獲取圖像中每一個像素點在真實空間中的x,y,z的值,使獲取的過程變得非常簡單方便。OpenNI2啟動的基本流程如圖2-6所示,代碼如下:openni::OpenNI::initialize();openni::DevicedevAnyDevice;

devAnyDevice.open(openni::ANY_DEVICE);openni::VideoStreamstreamDepth;

streamDepth.create(devAnyDevice,openni::SENSOR_DEPTH);

streamDepth.start();openni::VideoFrameRefframeDepth;streamDepth.readFrame(&frameDepth);constopenni::DepthPixel*pDepth

=(constopenni::DepthPixel*)frameDepth.getData();streamDepth.destroy();devAnyDevice.close();openni::OpenNI::shutdown();圖2-6OpenNI2使用基本流程值得注意的是,OpenNI2與KinectSDK的數據流相沖突,因此不能同時在兩個環境下獲取數據,本次試驗中,我們將兩個環境分別寫在了兩組程序中,讓被測試人員保持不動,機體也保持不動,分別運行兩個程序,獲取兩組數據。由于前后我們都設置了圖像的分辨率為640*480,因此數據的對齊也十分簡單。定義一個640*480*3的三維數組,便可以將整個環境中的點云數據輸出了,代碼如下所示:pdepth=(DepthPixel*)frameDepth.getData(); for(inti=0;i<frameDepth.getHeight();i++) { for(intj=0;j<frameDepth.getWidth();j++) {depthv=pdepth[i*frameDepth.getWidth()+j];CoordinateConverter::convertDepthToWorld(streamDepth,i,j,depthv,&x,&y,&z); xyzdata[i][j][0]=x; xyzdata[i][j][1]=y; xyzdata[i][j][2]=z; }}上述過程獲取了整個測量空間的點云數據,而我們需要的只是其中關于人體的點云數據,這就要我們根據之前從深度圖像中提取并優化過的人體數據,從點云中篩選出我們需要的部分。由于我們優化過的深度圖像中,無用信息的像素值是用黑色表示的,并且分辨率也是相同的,因此,只需要提取出深度圖像中有顏色部分的點云值,就很方便的完成了任務。具體的流程如圖2-7所示: 圖2-7點云提取流程經過篩選以后,我們就獲得了人體的點云數據了,將這些點用opengl畫出來,可以得到圖2-8所示的圖像,整個人的輪廓清晰可見。圖2-8提取的點云圖

第3章骨骼數據的處理3.1骨骼數據的獲取需要根據人體的動作改變目標點云的位置,那么骨骼的信息是必不可少的,第2章中提到,Kinect最多可以跟蹤兩個骨骼,可以最多檢測六個人。站立模式可以跟蹤20個關節點,坐著的模式的話,可以跟蹤10個關節點。如圖3-1所示:圖3-1Kinect骨骼跟蹤示意圖在KinectSDK中,對骨骼的跟蹤具有兩種模式,從圖3-1也可以看出,一種是全身骨架20個點的追蹤,被稱為主動跟蹤:另一種只能顯示感知用戶的位置,用一個點表示,這個點被安排在了用戶的質心位置。本文的研究中,只需要獲取一組人體的骨骼信息,因此,在骨骼獲取的時候,不能有干擾的對象存在,輸出多余的點信息。上一章還提到過,我們獲取Kinect中的信息時,都是從Frame類里提取的。當然,當我們通過Source和Reader初始化并打開了骨架數據流后,骨架數據流中的每一幀數據都被輸送到SkeletonStream中并形成一個集合,其中包含了骨架數據的所有信息。這些信息中,存在著一個存儲每一個骨架點的數組,一共有六組,如果沒有識別到足夠多的對象,那么剩余數組中的數據就會用0表示。這個數組中,包含了獲取對象中每一個關節點的信息、位置、關節的數據等,都用一個唯一的標識符來代表,列如HIP_CENTER,SPINE,SHOULDER_CENTER,HEAD,SHOULDER_LEFT等。Kinect能夠追蹤到的骨骼數量是一個常量。因此我們可以直接為這些數據分配一定大小的內存,而不用進行計算。循環遍歷skeletonFrame,每一次處理一個骨骼。當然,由于系統的某些缺陷,獲取信息的時候人體必須要完全暴露在Kinect的視野范圍內,否則出現了遮擋、畸形等情況,都會使得跟蹤難以進行。所以,我們在對這些骨骼信息進行操作之前,有必要對這個骨骼是否完整,是否能正確進行追蹤進行判斷。在這里,KinectSDK中也為我們提供了一個TrackingState屬性,它是基于每一個對象生成的,可以判斷這個對象是否能被追蹤。那么,經過了判斷以后,我們就可以做到只提取那些正確完整的的骨骼,而將那些混淆視聽的骨骼數據給忽略掉。判斷的時候,只需要舍棄TrackingState不等于SkeletonTrackingState.Tracked的骨骼數據就可以了。這樣一來,就可以快速忽略掉可能被系統誤認為是人體的一部分物體,減少了很多干擾因素。HANDLEskeletonEvent=CreateEvent(NULL,TRUE,FALSE,NULL);hr=NuiSkeletonTrackingEnable(skeletonEvent,0);if(FAILED(hr)){cout<<"Couldnotopencolorimagestreamvideo"<<endl;NuiShutdown();returnhr;}namedWindow("skeletonImage",CV_WINDOW_AUTOSIZE);while(1){NUI_SKELETON_FRAMEskeletonFrame={0};boolbFoundSkeleton=false;if(WaitForSingleObject(skeletonEvent,INFINITE)==0){hr=NuiSkeletonGetNextFrame(0,&skeletonFrame);if(SUCCEEDED(hr)){//NUI_SKELETON_COUNTfor(inti=0;i<NUI_SKELETON_COUNT;i++){NUI_SKELETON_TRACKING_STATEtrackingState=skeletonFrame.SkeletonData[i].eTrackingState;if(trackingState==NUI_SKELETON_TRACKED){bFoundSkeleton=true;}}}if(!bFoundSkeleton){continue;}for(inti=0;i<NUI_SKELETON_COUNT;i++){if(skeletonFrame.SkeletonData[i].eTrackingState==NUI_SKELETON_TRACKED&&skeletonFrame.SkeletonData[i].eSkeletonPositionTrackingState[NUI_SKELETON_POSITION_SHOULDER_CENTER]!=NUI_SKELETON_POSITION_NOT_TRACKED){floatfx,fy;for(intj=0;j<NUI_SKELETON_POSITION_COUNT;j++){NuiTransformSkeletonToDepthImage(skeletonFrame.SkeletonData[i].SkeletonPositions[j],&fx,&fy);skeletonPoint[i][j].x=(int)fx*2;skeletonPoint[i][j].y=(int)fy*2;}for(intj=0;j<NUI_SKELETON_POSITION_COUNT;j++){if(skeletonFrame.SkeletonData[i].eSkeletonPositionTrackingState[j]!=NUI_SKELETON_POSITION_NOT_TRACKED){circle(skeletonImage,skeletonPoint[i][j],3,cvScalar(0,255,255),1,8,0);tracked[i]=TRUE;}}drawSkeleton(skeletonImage,skeletonPoint[i],i);}}imshow("skeletonImage",skeletonImage);//顯示圖像}else{cout<<"Bufferlengthofreceivedtextureisbogus\r\n"<<endl;}}NuiShutdown();return0;}在上述獲取骨骼信息的代碼中,值得一提的是,SkeletonPositions[20]就是我們保存骨骼信息的數組。然而,這個數組也是有他的順序的,這大大方便了我們之后要進行的骨骼配準,具體的順序如下:HIP_CENTER,SPINE,SHOULDER_CENTER,HEAD,SHOULDER_LEFT,ELBOW_LEFT,WRIST_LEFT,HAND_LEFT,SHOULDER_RIGHT,ELBOW_RIGHT,WRIST_RIGHT,HAND_RIGHT,HIP_LEFT,KNEE_LEFT,ANKLE_LEFT,FOOT_LEFT,HIP_RIGHT,KNEE_RIGHT,ANKLE_RIGHT,FOOT_RIGHT,COUNT。其中,最后一個COUNT記錄了是第幾個骨架數據,可以不用輸出。骨架信息及獲取的骨架圖如圖3-2所示。圖3-2骨架對應點及獲取的骨架圖最后,我們不能忽略了一點,就是Kinect每一個數據流的深度圖、彩色圖、骨架圖,的坐標都是相對獨立的,并不能直接對應在一起,這也是KinectSDK的問題之一。因此,如果我們要獲取相對應的骨架點空間坐標,必須將其轉化到之前獲取的深度信息的坐標系中,這里就需要用到一個coordinateMapper類,具體代碼為:m_pCoordinateMapper->MapCameraPointToDepthSpace(joints[j].Position,&depthSpacePosition[j])這樣之后,我們再輸出獲取的數據,便可以將對應于深度圖像的骨架點坐標完整的提取出來了。3.2骨骼數據的校準在本文的研究中,我們使用一個標準人體模型,并且這個模型已經做了骨架點的提取。如圖3-3所示,我們可以發現,提取的骨架點的數據與我們從Kinect中獲取的骨骼點個數并不相同,這就造成了骨架點對應的困難。因此,接下來的工作就是要從這些骨架點中找出最接近于我們之前獲取的骨架點的16個點(由于標準人體模型骨架中明顯不存在WRIST、ANKLE這四個點,因此將他們從骨架中刪去),本文使用了kd-tree解決了這個問題。圖3-3使用的標準人體模型及其骨架3.2.1kd-tree算法簡介kd-tree是一種可以在高維空間中利用的,可以快速查出最鄰近點的一項查找技術。其全稱為K-dimensionaltree,其中K代表了使用的維度。顧名思義,這是一種樹形的數據結構,因此可以滿足檢索、插入、刪除等等的基本功能。這項技術尤其可以利用在數據量龐大的情況下,以及高維度的數據下的最鄰近匹配。進行特征點或是特征向量的提取。Kd-tree事實上是一個二叉樹,其中存放了一些需要用到的K維數據。這個二叉樹可以簡單看成對K維空間的一個劃分。列如二維空間中,就是對目標空間的面積劃分。在高維空間中,這個劃分中的每一個節點形成一個超矩形區域。那么,要想知道Kd-tree的有關算法,我們必須要首先了解一下二叉查找樹(BST)的相關算法以及概念:若它的左子樹不為空,則左子樹上所有結點的值均小于它的根結點的值;若它的右子樹不為空,則右子樹上所有結點的值均大于它的根結點的值;它的左、右子樹也分別為二叉排序樹。如圖3-4就是一個簡單的二叉查找樹。圖3-4二叉查找樹根據其定義,我們就可以知道怎樣構建一個二叉查找樹,我們可以將所要構建的數據信息一次插入到一個二叉樹中,并且保正經過插入以后,形成的二叉樹還是一個二叉查找樹。那么,我們只需要保證在每一個根節點左邊的子節點中的數據都小于根節點中的數據,而右邊的子節點的數據則大于根節點的數據,而通過這種方式構建出來的樹有且只有一種。于是,當我們需要從這個二叉樹中搜索我們想要的數據時,只需要從二叉查找樹的根節點開始,將我們需要的數據與其逐個進行比較,若大于節點數據,則轉移到此節點右邊的子節點繼續進行比較,若小于節點數據,則移到左邊子節點比較。Kd-tree的構建方式與此類似,只是其對應的維度數是大于等于一維的。所以,我們需要考慮的就是,我們通過什么樣的條件可以判斷將一個K維的數據,并唯一地將其分配到根節點的左邊或者右邊。類似的,當維度是一維的的時候,我們做的比較就比較直觀,因為只有一個數字比較。那么,當維度數增加了以后,我們就需要將整個的K維數據相互進行比較,不過在這里,Kd-tree只選擇這個多維數據其中的某個維度Ki進行比較,也就是說,在每一個節點中,都有一個法則,只會選擇某一維度的Ki進行比較。形象地說,就是用一個超平面經過某一個值將整個K維空間分割成兩部分。其中,這個超平面的一邊的數據中的Ki都小于根節點中Ki的值,而另一邊的數據中則相反。然后,如果兩個子節點后面還有其他的數據,那么就繼續進行劃分,直到最后所有子空間中都沒有數據為止,那么這個Kd-tree就算完成了。如圖3-5所示是一個三維的Kd-tree劃分。圖3-5三維Kd-tree劃分顯然,在劃分過程中需要注意兩個問題:1.在K維數據中選擇哪一個維度的數據為標準進行劃分;2.為了使計算的復雜度盡可能減小,快速的檢索樹,怎樣做才能使每一個子空間中劃分出的數據個數盡量相等。由于本次試驗中,我們只要對骨架數據簡單地進行二維的匹配并尋找相近點就可以完成任務,因此這一部分內容簡要概括一下。對于第一個問題,最簡單的方法就是一個接著一個維度來劃分,就比如說一個三維數據,每一次分子節點的時候,都根據順序先比較x值,下一節點比較y的值,在下一節點比較z的值,如此循環直到結束。這樣做就有一個不好的地方,因為每個維度數據的范圍都不是一樣的。還是拿三維的例子來說可能其中z軸對應的范圍比較小,視覺上來看也就是非常扁,這樣的話,我們就要引入另一種方法:最大方差法。顧名思義,計算每個維度數據的方差,方差大的變化也就大,反則反之。因此,根據方差的大小合理分配每個維度分塊的頻率,就能最大限度做到合理劃分。對于第二個問題,解決方法也顯而易見,若要做到每個葉子節點上的數據個數近乎相同,只要在每次確定好劃分那個維度上的數據時,計算這個維度上的數據量,按照大小排列好,最后取其中間的值作為劃分的標準,也就是根節點。有了這個條件以后,每次劃分我們都挑一個數出來作為下一個根節點,而不是隨意取一個數據代入。這樣就能很好地平衡每一個葉子節點上的個數了。明確了這兩個問題以后,Kd-tree的構建就變得十分簡單了如圖3-6所示:算法:構建k-d樹(createKDTree)輸入:數據點集Data-set和其所在的空間Range輸出:Kd,類型為k-dtree1.IfData-set為空,則返回空的k-dtree2.調用節點生成程序:(1)確定split域:對于所有描述子數據(特征矢量),統計它們在每個維上的數據方差。以SURF特征為例,描述子為64維,可計算64個方差。挑選出最大值,對應的維就是split域的值。數據方差大表明沿該坐標軸方向上的數據分散得比較開,在這個方向上進行數據分割有較好的分辨率;(2)確定Node-data域:數據點集Data-set按其第split域的值排序。位于正中間的那個數據點被選為Node-data。此時新的Data-set'=Data-set\Node-data(除去其中Node-data這一點)。3.dataleft={d屬于Data-set'&&d[split]≤Node-data[split]}Left_Range={Range&&dataleft}

dataright={d屬于Data-set'&&d[split]>Node-data[split]}

Right_Range={Range&&dataright}4.left=由(dataleft,Left_Range)建立的k-dtree,即遞歸調用createKDTree(dataleft,Left_Range)。并設置left的parent域為Kd;

right=由(dataright,Right_Range)建立的k-dtree,即調用createKDTree(dataleft,Left_Range)。并設置right的parent域為Kd。圖3-6構建Kd-tree方法3.2.2利用Kd-tree查找最鄰近點了解了如何構建Kd-tree以后,我們就可以利用它存儲我們需要提取的骨架數據了。由于標準模型是正對攝像機的,并且我們在獲取人體骨架的時候也特意獲取了正面的鏡頭(下一章點云配準也將會利用這一特性),因此我們構建一個二維的Kd-tree就可以實現骨骼點的提取。首先,我們要將兩幅骨架的x、y值經由平移放縮到相對重合的位置。我們可以看到,兩幅骨架中度數最高的兩個點:SHOULDER_CENTER及HIP_CENTER是可以直接對應的,因為在兩幅骨架中符合這個要求的只有這兩個點。于是,我們就可以將這兩個點看成一個線段,并且根據這兩個點的平移縮放,進行簡單的仿射變換將兩個骨架近似地重合在一起。如圖3-7所示,代碼如下:char*source_window="Sourceimage";

char*warp_window="Warp";

char*warp_rotate_window="Warp+Rotate";

intmain(intargc,char**argv)

{

Point2fsrcTri[3];

Point2fdstTri[3];

Matrot_mat(2,3,CV_32FC1);

Matwarp_mat(2,3,CV_32FC1);

Matsrc,warp_dst,warp_rotate_dst;

src=imread("Lena.jpg");

warp_dst=Mat::zeros(src.rows,src.cols,src.type());

srcTri[0]=Point2f(0,0);

srcTri[1]=Point2f(src.cols-1,0);

srcTri[2]=Point2f(0,src.rows-1);

dstTri[0]=Point2f(src.cols*0.0,src.rows*0.33);

dstTri[1]=Point2f(src.cols*0.85,src.rows*0.25);

dstTri[2]=Point2f(src.cols*0.15,src.rows*0.7);

warp_mat=getAffineTransform(srcTri,dstTri);

warpAffine(src,warp_dst,warp_mat,warp_dst.size());

Pointcenter=Point(warp_dst.cols/2,warp_dst.rows/2);

doubleangle=-50.0;

//doublescale=0.6;

doublescale=1;

rot_mat=getRotationMatrix2D(center,angle,scale);

warpAffine(warp_dst,warp_rotate_dst,rot_mat,warp_dst.size());

namedWindow(source_window,CV_WINDOW_AUTOSIZE);

imshow(source_window,src);

namedWindow(warp_window,CV_WINDOW_AUTOSIZE);

imshow(warp_window,warp_dst);

namedWindow(warp_rotate_window,CV_WINDOW_AUTOSIZE);

imshow(warp_rotate_window,warp_rotate_dst);

waitKey(0);

return0;

}圖3-7經過重合的兩組骨架接著,我們就可以開始二維Kd-tree的鄰近算法了。首先,從Kd-tree的根節點開始進行查詢,這和二叉搜索樹的查找方式一樣,順著節點依次作比較,一直查找到最后的葉子節點為止。在查找的時候,遵循每一個節點比較準則,即在某個節點是以Ki這個維度進行比較的,那么就提取出搜索數據的第Ki個維度,然后進行比較,若小于這個節點的值,就向左邊的分支繼續搜索,若大于節點值,則向右邊的分支繼續搜索。當搜索完畢后,算出查找到的數據與原始數據的距離,并先記錄這個點為最小距離的點。此后,由于是找尋最鄰近的點,因此只經過一次搜索并不能保證其距離最小,這個時候,就要進行回溯(Backtracing)操作,為了找尋可能的與目標數據更接近的點。也就是找尋那些搜索時沒有經過的子樹,這時候就要進行判斷,該子樹中有沒有可能會有距離更近的點,否則建立這個樹就失去了意義。我們可以有兩種方式來求目標數據與樹分支之間的距離。第一種是在構造樹的過程中,就記錄下每個子樹中包含的所有數據在該子樹對應的維度k上的邊界參數[min,max];第二種是在構造樹的過程中,記錄下每個子樹所在的分割維度k和分割值m,(k,m),目標點Q與子樹的距離則為|Q(k)-m|。經過判斷以后,如果可能有,那么就在新的分支上重復剛開始的搜索動作,當搜索到葉子節點時,比較其距離與已知的最近距離是否更小,如果更小,則更新最近點。如果沒有,那就繼續判斷其他的分支。當然,回溯不是永無止境的,他有一個從上往下的規律,當回溯到根節點時,已經不存在比所得數據更小的分支時,那么回溯就結束了,我們最終獲取的點也就是最鄰近點。其核心代碼如下:KDTree::_HyperRectangleKDTree::ExamplarSet::calculateRange()

{

assert(_size>0);

assert(_dims>0);

_Examplarmn(_dims);

_Examplarmx(_dims);

for(intj=0;j<_dims;j++)

{

mn.dataAt(j)=(*this)[0][j];

mx.dataAt(j)=(*this)[0][j];

}

for(inti=1;i<_size;i++)

{

for(intj=0;j<_dims;j++)

{

if((*this)[i][j]<mn[j])

mn[j]=(*this)[i][j];

if((*this)[i][j]>mx[j])

mx[j]=(*this)[i][j];

}

}

_HyperRectanglehr(mx,mn);

returnhr;

}std::pair<KDTree::_Examplar,double>KDTree::KDTree::findNearest_i(KDTreeNode*root,_Examplartarget)

{

KDTreeNode*pSearch=root;

std::vector<KDTreeNode*>search_path;

_Examplarnearest;

doublemax_dist;

while(pSearch!=NULL)

{

search_path.push_back(pSearch);

ints=pSearch->splitDim();

if(target[s]<=pSearch->getDomElt()[s])

{

pSearch=pSearch->_left_child;

}

else

{

pSearch=pSearch->_right_child;

}

}

nearest=search_path.back()->getDomElt();

max_dist=Distance_exm(nearest,target);

search_path.pop_back();

while(!search_path.empty())

{

KDTreeNode*pBack=search_path.back();

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論