標籤:提取演算法 指定 因此 關係 計算 html member 模型 ffffff
本文討論的範圍不包含幾何重建、紋理重建,僅包含基於已有的人體三維三角網格模型(下稱人體三角網格)的三維測量演算法。圍度測量實際上是對3D 模型與特定平面求交,並對交集進一步分析其交集的特定閉合部分(若存在多個閉合部分)的輪廓周長和面積等資料,即求三維網格的切片。國標GB/T 16160-2008《服裝用人體測量的部位及方法》中給出了人體各尺寸特徵的與人身高的比例資料(下稱特徵比例),求出特定比例處與身高方向構成的點法式平面與三維網格的切片即可進一步得到特定特徵的具體數值。
1.1 人體三角網格的座標系矯正
假定人體三角網格的座標系中Z軸方向為人角的中點與頭頂中心構成的方向向量,而手持掃描器擷取到的人體三角網格的座標系取決於首次掃描的朝向,因此網格的Z軸方向與正確的座標系有誤差,需要通過額外的工作進行座標軸的矯正。
本文採用的核心方法為PCA(Principal Component Analysis)。對於手臂與身體軀乾的面元粘連問題,可通過掃描目標張開手臂進行規避,而此時掃描目標人站立並張開手臂時的高低肩會影響PCA座標矯正的效果。影響PCA演算法的主要部分為手臂部分三維點,去除手臂部分三維點進行二次PCA演算法即可。簡單地可以通過特定的比例關係選取,亦可通過三維點到Z軸的距離選取。
1.2 三角網格切片演算法1.2.1 基於排序的三角網格切片演算法
基於三角網格的切片演算法可分為兩類。一是基於半邊資料結構的三角網格切片演算法,假定半邊結構半邊對象為:
struct HE_edge{ HE_vert* from; HE_vert* to; HE_edge* pair; // oppositely oriented adjacent half-edge HE_edge* next; // next half-edge around the face ... // other members};
並且此時擷取到了平面plane與指定片面相交的起始半邊s, 可知演算法的結果為從s出發的一個半邊序列,那麼演算法可以簡單描述為HE_Slice(s->pair,*),通過迴圈去遞迴可進一步最佳化:
void HE_Slice(HE_edge* curr, std::vector<glm::vec3>& res){ if(curr->next == s || curr->next->next == s) return ; for(auto e: {e->next, e->next->next}) { if(is_intersect(e,plane)) { ... // calculate and store the intersection HE_Slice(e->pair, res); } }}
1.2.2 基於排序的三角網格切片演算法
基於半邊資料結構的切片演算法是高效的,但半邊資料結構的維持需要較多的記憶體支援且構建耗時_O(n*log(n))_、只提供了局部拓撲資訊不能支援按身高比例高效擷取特定半邊。上述兩個缺點均可通過下面介紹的面元排序解決。考慮到人體三角網格與平面plane的交集可以為多個閉合部分(手與軀幹、雙腳),半邊結構能且只能擷取到給定的初始半邊所在閉合部分,不能獲得指定平面與三角網格的交集的閉合部分的數量(下稱閉合數)。演算法架構如下:
1. 對共用頂點結構的三角網格進行排序2. 通過二分尋找確定需要與平面求交的有序面元的最小子序列3. 遍曆子序列,產生平面與三角形面元的邊的交的集合,集合元素為{A,B,k},其中A,B為邊的頂點索引,k為相交係數。4. 通過選擇排序對3.產生的集合完成有序化,並得到閉合部分的劃分。
參與切片演算法的最小几何元素為邊,因此對三角網格排序應當等價於對三角形面元排序。排序演算法簡單描述如下:
// 1. 對共用頂點結構的三角網格進行排序void sortByVector(TriMesh *mesh, glm::vec3 x) { std::sort(mesh->f.begin(), mesh->f.end(), [=](glm::ivec3& e1, glm::ivec3& e2){ return glm::dot(mesh->v[e1[0]] - mesh->v[e2[0]], x) > 0; });}
易知實際中參與切片演算法的面元是排序後的面元序列的一個子序列,當給出切片平面(點法式平面,法向量為Z軸)和面元在Z軸上的最大投影距離_gap_時,子序列提取演算法如下:
// 2. 通過二分尋找確定需要與平面求交的面元的最小有序子序列std::array<std::vector<glm::ivec3>::iterator,2> getSliceInterval(TriMesh* mesh,glm::vec3 n, float d, float gap) { std::array<std::vector<glm::ivec3>::iterator,2> result = { std::lower_bound(mesh->f.begin(),mesh->f.end(), d-gap, [=](glm::ivec3& e1,float v){ return v < glm::dot(mesh->v[e1[0]], n); }), std::lower_bound(mesh->f.begin(),mesh->f.end(), d+gap, [=](glm::ivec3& e1,float v){ return v < glm::dot(mesh->v[e1[0]], n); })}; if(result[0] > result[1]) std::swap(result[0],result[1]); return result;}
確定子序列的邊界,切片演算法便只需要極少的面元與特定平面求交。面元與平面的相交判定與交線等幾何計算如下:
bool isFaceInersected(TriMesh* mesh,glm::ivec3 f, glm::vec3 n, float d){ int flags = ((glm::dot( mesh->v[f[0]], n ) > d)+(glm::dot( mesh->v[f[1]], n ) > d)+(glm::dot( mesh->v[f[2]], n ) > d)); return flags == 1 || flags == 2;}std::array<glm::vec3,2> getFaceIntersection(TriMesh* mesh,glm::ivec3 f, glm::vec3 n, float d){ std::array<glm::vec3,2> result; int size = 0; // dot(N, P1) + dot(N, t*(P1-P2) ) = d // 0 < t < 1 for(int i:{0,1,2}) { double numerator = d - glm::dot( mesh->v[f[i]], n); double denominator = glm::dot( mesh->v[f[i==2?0:i+1]] - mesh->v[f[i]], n ); if(abs(denominator) > 1e-8) { numerator /= denominator; if(numerator >= 0 && numerator <= 1.0) { result[size++] = glm::vec3(f[i],f[i==2?0:i+1],numerator); } } } return result;}
3.得到的線段是無序的,不能夠滿足後續操作,如閉合數、特定閉合部分的平滑、特定部分的周長面積等幾何特徵的計算,故需要對3.的結果有序化。考慮到實際網格規模下的交線的規模往往低於10000,演算法設計採用了選擇排序,演算法複雜度為_O(n*log(n))_,簡單描述如下:
//4. 通過選擇排序對3.產生的集合完成有序化,並得到閉合部分的劃分。std::vector<std::array< std::vector<std::array<glm::vec3,2>>::iterator,2>> sortContours(std::vector<std::array<glm::vec3,2>>&& intersections) { std::vector<std::array< std::vector<std::array<glm::vec3,2>>::iterator,2>> intervals = {{intersections.begin(),intersections.begin()}}; for(auto it = intersections.begin(); it != intersections.end(); it++) { auto find_it = std::find_if(std::next(it), intersections.end(), [=](std::array<glm::vec3,2>& e){ return (e[0][0] == (*it)[1][1] && e[0][1] == (*it)[1][0]) || (e[0][0] == (*it)[0][1] && e[0][1] == (*it)[0][0]) ||(e[1][0] == (*it)[0][1] && e[1][1] == (*it)[0][0]) || (e[1][0] == (*it)[1][1] && e[1][1] == (*it)[1][0]); }); if(find_it != intersections.end()) { std::swap(*std::next(it),*find_it); }else{ (*std::prev(intervals.end()))[1] = std::next(it); if(it != std::prev(intersections.end())) intervals.push_back({std::next(it),std::next(it)}); } } return intervals;}
到此,
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
最新機動車行車執照模板PSD可編輯分層檔案下載
基於三角網格排序的人體三圍測量演算法