如何實現(xiàn)3D物品自動擺放,構(gòu)建一個宜居的家居空間,這篇文章將告訴你答案!文末給出相關(guān)資料~
一、生成步驟
分兩步生成給定房間的家具布置:
第一步:根據(jù)美學(xué)和功能規(guī)則,優(yōu)化房間中家具對象的選擇和布置。此過程通過【貪婪成本最小化算法】快速探索家具布局的無限跨維空間
根據(jù)室內(nèi)設(shè)計規(guī)則形成的成本函數(shù)優(yōu)化全局家具布局和對象選擇。在此優(yōu)化過程中,家具布置會因特定動作而隨機改變,并且只有當(dāng)新配置的成本低于原始室內(nèi)設(shè)計的成本時,才接受新配置。
第二步:【程序方法】以隨機方式局部應(yīng)用以生成重要的場景細節(jié)
最終的設(shè)計通過較小的物體進行局部增強,以生成重要的場景細節(jié)。為此,我們使用一套程序,每個程序都針對特定的局部布置,例如桌子上的盤子、開口處的窗戶等。
最后,通過材料優(yōu)化實現(xiàn)統(tǒng)一的設(shè)計和和諧的色彩分布。
二、室內(nèi)布局自動化
自動室內(nèi)設(shè)計方法結(jié)合了優(yōu)化和程序方法,為給定的虛擬房間生成宜居的室內(nèi)設(shè)計。首先,大型家具對象的最優(yōu)布局是通過貪婪成本優(yōu)化算法生成的。在第二步中,使用程序方法來局部布置小物體(即房間裝飾)。
貪婪成本優(yōu)化和程序裝飾的好處是我們的方法實現(xiàn)了全自動室內(nèi)設(shè)計的交互速度。
1.空間組件布置優(yōu)化
此過程使用貪婪成本最小化算法,在優(yōu)化過程中用數(shù)據(jù)庫中的家具對象填充虛擬房間。此優(yōu)化的主要目標是為給定房間找到成本函數(shù)最小的家具布置。
成本函數(shù)的最小化工作原理如下:
-
首先,使用給定房間的適當(dāng)對象隨機生成家具布局。
-
然后,計算該布局的成本函數(shù)。
-
接下來,家具布局將通過一組移動(突變)進行更改。
-
最后,如果新配置的成本低于之前的配置,則可以接受。
-
通過迭代執(zhí)行對成本函數(shù)的評估和新設(shè)計的生成,以達到最佳的家具布局。
1.1 類別組件屬性定義
為了更好的應(yīng)用算法執(zhí)行如上過程,我們對家具定義成對象或組件,算法需要有關(guān)用于自動布局的組件的屬性和關(guān)系的信息。為了簡化向系統(tǒng)中添加新組件的過程,我們使用組件類別的概念并按類別存儲所需的屬性。然后,每個組件只需要表明它屬于哪個類別,這些類別對應(yīng)于組件的類型,例如“衣柜”或“桌子”。
來自一類的所有組件共享用于計算成本函數(shù)的相同屬性。這些屬性對應(yīng)于專業(yè)設(shè)計實踐中使用的測量和關(guān)系。我們的方法中針對每個類別的組件使用以下屬性:
組件 |
屬性類型 |
屬性 |
說明 |
建議參數(shù) |
舉例 |
類型 |
常量 |
間隙約束 |
前、后、左、右。間隙約束指定組件周圍的舒適使用所需的空白空間。 |
? | ? |
靠墻站立的概率 |
指定了一個物體站在墻附近的重要性 |
[0,1] |
? | ||
可能的父母 |
包含組件類別列表,這些組件類別可能是當(dāng)前組件的父對組件。此外,使用到父組件的最小和最大距離,并指定朝向父組件的方向(正面或側(cè)面)。 |
objlist |
? | ||
有父母的概率 |
表示組件與其他組件處于組關(guān)系中的重要性 |
[0,1] |
? | ||
房間重要性 |
說明組件對于特定空間的重要性。 |
[0,1] |
臥室中必須有一張床,因此該房間的重要性為 1.0。 |
||
所需計數(shù) |
每個類別都包含特定空間中該類別的最小要求和最大允許數(shù)量 |
spacetype - [min, max] |
起居室最多可以放置一臺電視機。 |
||
變量 |
位置 |
空間坐標 |
? | ? | |
形狀 |
可以是3D包圍盒 |
? | ? | ||
朝向 |
地平面中的 2D 矢量 |
? | ? |
1.2 空間屬性定義(特殊組件)
空間 |
屬性 |
說明 |
建議參數(shù) |
舉例 |
類型 |
位置 |
世界坐標 |
? | ? |
形狀 |
TODO:具體表示方式,調(diào)研中 |
? | ? |
1.3 組件布局調(diào)整策略
1. 隨機改變組件的位置。
2. 隨機改變組件的方向。
3. 將組件對象與最近的組件對齊。
4. 將組件與最近的墻壁對齊。
5. 將組件捕捉到最近的組件。
6. 將組件固定到最近的墻壁上。
7. 將組件與可能的父組件之一連接。
8. 向父組件添加新的子組件。
9. 將新組件添加到布局中。
10. 從布局中隨機移除組件。
每一次迭代,如上策略的調(diào)整都以特定的概率下執(zhí)行。在我們的實驗中,使用以下經(jīng)驗值設(shè)置這些概率:
- 1至5:適用于每個組件,概率為0.3
-
6:對每個對象執(zhí)行,概率為特定組件靠墻站立概率值
-
7:具有為特定屬性組件定義有父對象及對應(yīng)的概率值
-
8:父組件添加新子組件概率為0.6
-
9:添加組件到布局概率為0.5
-
10:從布局中隨機刪除的概率0.1
-
特別說明:動作8和9往布局中添加過程中,使用特殊啟發(fā)式方法來實現(xiàn)子組件圍繞父組件的對齊(例如:桌子周圍的椅子)。此啟發(fā)式將子組件定位到父中間件的相對側(cè),并根據(jù)其計數(shù)屬性值對齊它們。且只接受不會導(dǎo)致組件相交的布局。
?
室內(nèi)設(shè)計的成本計算均使用設(shè)計指南條款的加權(quán)和,該算法迭代地優(yōu)化家具布局
基于遺傳算法的處理策略:
選擇(Selection):
我們的遺傳算法優(yōu)化通過從當(dāng)前群體創(chuàng)建新一代家具布局來推進每次迭代。為了選擇能夠生存到下一代的設(shè)計個體,我們使用錦標賽選擇。在比賽選擇中,每個存活下來的個體都通過兩個步驟來確定:首先,從島上的子種群中隨機選擇 ?? 個體。其次,從這些 ?? 選出的個體中,成本值最低的個體是錦標賽的獲勝者,并進入下一代。我們在實現(xiàn)中設(shè)置 ?? = 6。通過這種方法,我們的算法選擇 70% 的群體進入下一代。剩下的 30% 是通過所選父母的交叉產(chǎn)生的。最后,我們對這個新創(chuàng)建的群體中的 50% 應(yīng)用突變。
?
交叉(Crossover):
交叉是一種類似于自然界繁殖的計算操作,其中通過組合兩個父母的基因組來創(chuàng)建新的后代。遺傳算法使用交叉從選定父母的隨機子部分創(chuàng)建新個體。在家具布局領(lǐng)域,個體是由家具對象的配置形成的。因此,通過從每個父布局中選擇隨機家具對象并將它們組合在一起形成新的家具布局,可以自然地進行交叉。在我們的方法中,30% 的新一代是由 70% 的選定個體交叉形成的。交叉操作的父母是隨機選擇的。通過將來自第一個父母的大約一半的家具物品與來自第二個父母的物品交換,形成一個新個體。如果第二個父對象的家具對象有子對象,這些子對象也會插入到新個體中。所選家具對象不會插入到新個體中,以防與現(xiàn)有對象發(fā)生交叉。
?
突變(Mutations):
在每次迭代結(jié)束時,我們的算法都會通過隨機突變來改變 50% 的設(shè)計個體,以支持探索新的家具配置。我們的方法使用以下突變來改變個體:
(1)隨機改變家具對象的位置。
(2)隨機改變家具對象的方向。
(3) 將家具對象與最近的對象對齊。
(4) 將家具對象與最近的墻壁對齊。
(5) 將家具對象捕捉到最近的對象。
(6) 將家具對象固定到最近的墻壁上。
(7) 將家具對象與可能的父對象之一連接。
(8) 向父對象添加新的子對象。
(9) 將新的家具對象添加到設(shè)計個體中。
(10)從設(shè)計個體中移除隨機對象。
詳情見1.3
2.成本函數(shù)
算法根據(jù)成本函數(shù)優(yōu)化組件布局,以達到給定空間最理想的組件布置,因此成本函數(shù)反映了專業(yè)設(shè)計師在實踐中使用的規(guī)則。成本函數(shù)中使用以下規(guī)則及其數(shù)學(xué)表達式:
2.1 清除(Clearance)
功能組件需要在它們周圍留出一個空白空間才能使用它們的主要功能。 有些組件需要從一側(cè)或多側(cè)直接訪問。 間隙準則代表了這一要求。 我們將間隙準則建模為由間隙約束擴展的對象邊界框之間的重疊量:
間隙表達式以成對的方式計算。b1和b2是集合??的擴展邊界框。集合??包含一個組件布局中所有組件的擴展邊界框,以及墻壁、窗戶和門的邊界框。函數(shù)V返回3D幾何形狀的體積,|??|表示集合 的大小。
# 計算方法:
def calcLayoutClearance(self , objList, layoutPoly= None, entList = None):
"""
calculating layout polygons mean overlap
objList - List of obstacle objects (polygons)
Each object is assumed to represent the EXTENDED-bounding-box, i.e., including the extra gap
required around the object
layoutPoly - Nx2 list of ordered vertices defining a 2D polygon of N vertices - room polygon layout
last point NEQ first point
entList - List of entrance line segments (2D points). Entrances should not be occluded
"""
#
# =>>>>> CURRENTLY constraints are not included, e.g. entrance, window, power-outlet, TV
#
nObj = len(objList)
objListSp = []
# Transform to shapely
for n in range(nObj):
objListSp.append(sgp.Polygon([[p.x, p.y] for p in objList[n]]))
ovlpSum = 0
for m in range(nObj - 1):
for n in range(nObj):
if m == n:
continue
ovlp = objListSp[m].intersection(objListSp[n]).area
ovlpSum += ovlp / objListSp[m].area
ovlpSum = ovlpSum / (nObj * (nObj - 1))
# ==> entrance overlap
# if entLine.touches(tmpPolyLayout) or entLine.intersects(tmpPolyLayout):
# ovlp = entLine.intersection(tmpPolyLayout).length / entLine.length
return ovlpSum
2.2 流通(Circulation)
流通指南表達了功能組件需要人類可以物理訪問以發(fā)揮其功能的需求,即空間的任何部分都不應(yīng)被阻塞以供人類居住和使用。我們通過無法從房間入口進入的物品數(shù)量來表達這一指導(dǎo)方針。為了計算這個數(shù)字,我們需要使用路徑查找算法并評估從入口到家具對象的路徑是否存在。
我們的方法使用回溯來尋找離散空間中的可能路徑。這通過三個主要步驟完成:
-
第一步:所有功能組件和墻壁都投影到地平面并光柵化為 2D 網(wǎng)格
-
第二步:對這些投影應(yīng)用擴張操作來解釋身體大小,此操作通過指定人體半徑的圓盤擴展離散投影
-
第三步:功能組件的正面在網(wǎng)格中被標記為從房間入口開始的路徑可以訪問的目標,然后開始回溯算法。
如上算法返回可訪問目標的數(shù)量??a,我們將當(dāng)前布局中存在的功能組件總數(shù)標記為????。流通準則的表達式可以寫成:
我們對總體中的所有布局設(shè)置了一個硬約束,以包含至少2個功能組件。 因此對于所有定義的方程,????>1成立,進而避免了除以零。
# 計算方法
def findPathPoly(self ,sourceP, targetP, objList, layoutPoly):
"""
calculating shortest path from sourceP point to targetP that avoid polygon shape obstacles
sourceP/targetP - 2D points
objList - List of obstacle objects (polygons, each is a list of 2D points).
Should Contains the object's polygon and forward facing edge ???
layoutPoly - Nx2 list of ordered vertices defining a 2D polygon of N vertices - room polygon layout
last point NEQ first point
=>>>>>>> Assuming polygons DO NOT intersect
"""
nObj = len(objList)
objListVg = []
# Transform to pyvisgraph format
for n in range(nObj):
tmpPoly = []
for p in objList[n]:
tmpPoly.append(pvg.Point(p.x,p.y))
objListVg.append(tmpPoly)
# Start building the visibility graph
graph = pvg.VisGraph()
refPoint = pvg.Point(sourceP[0].x, sourceP[0].y)
workers = 1
graph.build_mod(objListVg, workers, None, refPoint) # , workers=workers)
# graph.build(objListVg) #, workers=workers)
# Get the shortest path
shortest_path = []
path_distance = []
direct_distance = []
for n in range(len(sourceP)):
sP = pvg.Point(sourceP[n].x, sourceP[n].y)
tP = pvg.Point(targetP[n].x, targetP[n].y)
spath = graph.shortest_path(sP, tP)
# Calculate the total distance of the shortest path
pdistance = 0
prev_point = spath[0]
for point in spath[1:]:
pdistance += np.sqrt((prev_point.y - point.y) ** 2 + (prev_point.x - point.x) ** 2)
prev_point = point
shortest_path.append(spath)
path_distance.append(pdistance)
dDist = np.sqrt((targetP[n].x - sourceP[n].x) ** 2 + (targetP[n].y - sourceP[n].y) ** 2)
direct_distance.append(dDist)
# print('Shortest path distance: {}'.format(path_distance))
return shortest_path, path_distance, direct_distance
def calcLayoutCirculation(self ,objList, srcList, tgtList):
"""
calculating layout polygons accessibility from entrance (could be more than one entrance)
objList - List of obstacle objects (polygons)
Each object is assumed to represent the EXTENDED-bounding-box, i.e., including the
extra gap
required around the object
src/tgt-List - pairs of points between which shortest path is calculated and compared to straight
path
"""
# print(objList)
# print(srcList)
nPairs = len(srcList)
pathRatioSum = 0
sPath, lenPath, dirPath = self.findPathPoly(srcList, tgtList, objList, [])
for n in range(nPairs):
pathRatioSum += (1 - dirPath[n] / lenPath[n])
return pathRatioSum
2.3 組關(guān)系(Group Relationships)
室內(nèi)設(shè)計中的功能組件可以根據(jù)其功能和類型進行分組。通常一組組件都有其父組件(例如:椅子位于桌子周圍), 組內(nèi)空間組織符合特定要求, 這些要求之一是人們的舒適交談。而舒適的談話取決于座位的位置,座位應(yīng)支持眼神交流和正常的語音音量(即有限的距離), 我們用同類組件的平均距離來表示組關(guān)系成本:
其中??r是房間在地平面中的對角線尺寸。??是房間中所有功能組件中心的集合。如果中心??1, ???2屬于同一類別的對象函數(shù)??(???1, ???2)返回 1,否則返回 0。
表示向量的大小。
# 計算方法
def calcGroupRelation(self ,objPos, membershipVec, dR):
"""
calculating object inter-group-relations: spread of objects from a group relative to space diagonal
objPos - vector of objects' center (numpy array)
membershipVec - vector of objects' membership association (integers)
dR - space diagonal (scalar)
"""
gSum = 0
nObj = len(objPos)
for i in range(nObj - 1):
for j in range(i + 1, nObj):
p = np.array(objPos[i]) - np.array(objPos[j])
p1 = np.array([p.x,p.y])
gSum += 1.0 * (not (membershipVec[i] - membershipVec[j])) * npla.norm(p1)
gSum /= ((nObj - 1) * nObj / 2 * dR)
return gSum
2.4 對齊(Alignment)
在室內(nèi)設(shè)計中,組件應(yīng)正確定向并與其支撐表面對齊(例如:櫥柜的背面應(yīng)朝向墻壁)。此外功能組件應(yīng)與其他對組件對齊。我們通過組件前向量之間角度的變化以及組件與其最近墻壁之間的概率距離測量來模擬對齊指南。對齊項以成對的方式評估:
a與兩個前向量??1和??2之間的角度成正比。??是所有功能組件的前向量集合。我們還將房間墻壁的前向量包含在??中以允許功能組件與墻壁對齊。 a-是a中所有向量的平均值。 等式5將??1和??2之間的角度的余弦從范圍 (-1,1)重新映射到 (0,1) 以用作成本函數(shù)的一項。
除了適當(dāng)對齊功能組件外,其中一些還應(yīng)靠墻站立, 因此我們將墻距離項??w添加到對齊成本中。 該術(shù)語使用為每個組件類別定義的靠墻pw的概率
~pb 是功能組件背面的點。 集合?? 表示來自當(dāng)前室內(nèi)設(shè)計中存在的所有功能組件的這些后點的集合。 函數(shù)projw(~pb)將點~pb投影到其最近的墻壁并返回此投影點。dr是房間在地平面中的對角線尺寸,如果房間不是矩形,那么dr被計算為房間邊界框的對角線大小。
# 計算方法
def calcAlignment(self,backPos, walls, wallProbVec, dR):
"""
calculating object alignment, currently only w.r.t. supporting wall
backPos - vector of objects' back position (numpy array)
walls - list of walls, each represented as a line (end points)
wallProbVec - probability vector of objects' to stand against the wall
dR - space diagonal (scalar)
"""
#
# ====> DIDNOT check direction, i.e. that object is parallel/perpendicular to wall
#
nW = len(walls)
nO = len(backPos)
wLines = []
for iW in range(nW):
wLines.append(sgls.LineString((walls[iW][0], walls[iW][1])))
wSum = 0
for iO in range(nO):
dP = np.array([])
for iW in range(nW):
# shortest distance to wall
dP = np.append(dP, wLines[iW].distance(spt.Point(backPos[iO])))
wSum += wallProbVec[iO] * min(dP)
wSum /= (nO * dR)
return wSum
def Aligment_transformation(self , objects_list , dR_room):
all_objects_front_center_points = []
all_objects_back_center_points = []
for item in objects_list:
x1 =int(item["point"].x)
y1 = int(item["point"].y)
x2 = x1 + int(item["furniture"].top_right.x)
y2 = y1 + int(item["furniture"].top_right.y)
if (self.rotation == 0):
front = self.midpoint(Point(x1,y1),Point(x2,y1))
back = self.midpoint(Point(x1,y2),Point(x2,y2))
elif (self.rotation == 1):
front = self.midpoint(Point(x1,y2),Point(x1,y1))
back = self.midpoint(Point(x2,y2),Point(x2,y1))
elif (self.rotation == 2):
front = self.midpoint(Point(x2,y2),Point(x1,y2))
back = self.midpoint(Point(x2,y1),Point(x1,y1))
elif (self.rotation == 3):
front = self.midpoint(Point(x2,y1),Point(x2,y2))
back = self.midpoint(Point(x1,y1),Point(x1,y2))
all_objects_front_center_points.append(front)
all_objects_back_center_points.append(back)
# all_objects_front_points.append(front)
walls = []
walls.append(((0, 0), (100, 0)))
walls.append(((100, 0), (100, 100)))
walls.append(((100, 100), (0, 100)))
walls.append(((0, 100), (0, 0)))
wallProbVec = np.array([0.2, 0.2, 0.4, 0.6,0.2,0.6])
# dR = 600
return (self.calcAlignment(all_objects_back_center_points,walls,wallProbVec,dR_room))
2.5 分布和節(jié)奏(Distribution and Rhythm)
根據(jù)該準則,功能組件應(yīng)在空間中適當(dāng)分布,并且這種分布的頻率應(yīng)遵循一定的節(jié)奏(例如:畫作應(yīng)該沿著墻上的一條線分布,它們之間的距離有節(jié)奏地重復(fù))。 我們將此準則的成本建模為組件對之間的相對距離的方差:
?? 代表兩點之間的相對距離,??m即歐幾里得距離除以場景中兩個組件之間的最大距離 。~d是室內(nèi)設(shè)計中所有組件對之間的平均相對距離。
def calcObjDistrib(self ,objPos):
"""
calculating object distribution in space, also referred to as Rhythm
"""
nObj = len(objPos)
# get all pairs distance
dP = np.array([])
for i in range(nObj - 1):
for j in range(i + 1, nObj):
p = np.array(objPos[i]) - np.array(objPos[j])
dP = np.append(dP, npla.norm(np.array([p.x, p.y])))
dMx = np.max(dP)
dP /= dMx
dPmean = np.median(dP)
dSum = 0
for n in range(len(dP)):
dSum += (dP[n] - dPmean) ** 2
dSum /= len(dP) # ((nObj-1)*nObj/2)
# ==>>>> Maybe calculate sqrt(dSum), i.e. the Sigma and not Variance
return dSum
2.6 視圖平截頭體(Viewing Frustum)
在優(yōu)化的布局中,一些組件的主要功能應(yīng)該從其他組件可見(例如:電視應(yīng)該從沙發(fā)上可見)。 在我們的方法中,這些組件符合父子關(guān)系。 因此我們通過在所有父子對之間投射光線來計算視錐體成本,并計算與其他功能組件相交的光線數(shù)量。 我們將相交光線的數(shù)量表示為 ???? ,將室內(nèi)設(shè)計中的物體總數(shù)表示為 ????。 視錐體成本可以計算為:
2.7 功能需求(Functional Needs)
空間中的功能組件用于該空間的特定功能或活動。 因此一個特定的空間應(yīng)該包含對這個空間的活動很重要的內(nèi)部物品(例如:客廳應(yīng)該包含電視和沙發(fā))。 我們對功能需求的表達由兩個術(shù)語組成:
等式10中的第一項與當(dāng)前設(shè)計個體中存在的所有功能組件的空間重要性有關(guān)。該術(shù)語將更高的成本分配給對當(dāng)前空間不太重要的組件。重要性成本總結(jié)了設(shè)計個體中存在的組件的重要性值 io。I 是這些重要性值的集合。第二項與房間中特定類別的對象的期望數(shù)量有關(guān)。函數(shù)?(????) 計算類別 ???? 的對象數(shù)與該類別的所需對象數(shù)之間的差異。集合 ?? 代表當(dāng)前室內(nèi)設(shè)計中存在的所有類別。
# 計算方法
def calcFunctionality(self,impVec, objCatNum, catDesNum):
"""
calculating objects functionality importance and quantity
impVec - vector of importance values
objCatNum - amount of objects from each category in the layout (dict)
catDesNum - desired amount of objects from each category (dict)
#CALLING
actual_amounts = {'Table':2,'Chair':2,'Bed':2}
desired_amounts = {'Table':1,'Chair':4,'Bed':1}
r1.calcFunctionality(r1.object_importance,actual_amounts,desired_amounts)
"""
nO = len(impVec)
fSum1 = np.sum(1-impVec)
fSum1 /= nO
fSum2 = 0
for oc in objCatNum.keys():
fSum2 += abs(objCatNum[oc] - catDesNum[oc])
fSum2 /= (1.0 * len(objCatNum))
fSum = 0.5 * (fSum1 + fSum2)
return fSum
2.8 占比(Proportion)
功能組件應(yīng)與特定空間以及彼此之間具有適當(dāng)?shù)谋壤?。此外如果空間內(nèi)有太多空置空間,則應(yīng)生成新組件。我們將此準則的成本建模為組件覆蓋的體積與空間體積的比率:
Vo 是所有功能組件的總體積,Vr 是空間的總體積。 將這兩個體積與組件覆蓋的體積所需的比例rv進行比較。我們在實驗中rv使用了0.45 值。 我們憑經(jīng)驗發(fā)現(xiàn)這些值最適合我們的優(yōu)化。 體積Vr 還取決于空間的高度。 因此,較高的空間最好根據(jù)比例準則選擇較高的功能組件。
# 計算方法
def calcProportion(self ,objList, roomPoints, desRatio = 0.45):
"""
Till now on the basis of area not volume
calculating layout-volume-to-room-ratio
objList: List of all points of each object in room
roomVol: Room points
"""
nObj = len(objList)
objListSp = []
# Transform to shapely
for n in range(nObj):
objListSp.append(sgp.Polygon([[p.x, p.y] for p in objList[n]]))
roomSp = sgp.Polygon([p.x, p.y] for p in roomPoints)
objVolSum = 0
for i in range(len(objListSp)):
objVolSum += objListSp[i].area
roomVol = roomSp.area
gP = max(desRatio - 1.0 * objVolSum / roomVol, 0) / (1.0 * desRatio)
return gP
2.9 黃金分割(Golden Section)
該術(shù)語基于藝術(shù)和設(shè)計中常用的黃金分割原理,這一原則建議對空間進行令人賞心悅目的細分。我們將空間矩形從每一側(cè)以黃金分割比例的線進行細分,分割率近似為 0.618。這四個細分線在設(shè)計中用作主要組件的位置。因此,我們根據(jù)功能組件與其最近的黃金分割線的距離來評估黃金分割成本:
~c 代表功能組件和項目的中心,projgs(~c)返回該中心到最近的黃金分割線的投影。
# 計算方法
def calcGoldenSec(self,objPos, roomRect, dR):
"""
calculating objects location w.r.t. golden section lines
objPos: objects' center position
roomRect: 4 points of room (or sub-area) rectangle
dR: room diagonal
"""
# make sure the vertices are ordered
tmpRect = sgp.Polygon([p.x, p.y] for p in roomRect)
tmpRect = tmpRect.convex_hull
t_rect = tmpRect.exterior.coords[0:-1]
# creating golden lines. Assuming gsRatio = 13/21
# go over the 2 consecutive pair of vertices and generate the 4-lines, 2 in each side
gsr = 13.0 / 21.0
line1 = sgls.LineString((t_rect[0], t_rect[1]))
length = npla.norm(np.array(t_rect[0]) - np.array(t_rect[1]))
pt11 = line1.interpolate(length * (1.0 - gsr))
pt12 = line1.interpolate(length * gsr)
line3 = sgls.LineString((t_rect[2], t_rect[3]))
length = npla.norm(np.array(t_rect[2]) - np.array(t_rect[3]))
pt32 = line3.interpolate(length * (1.0 - gsr))
pt31 = line3.interpolate(length * gsr)
line2 = sgls.LineString((t_rect[1], t_rect[2]))
length = npla.norm(np.array(t_rect[1]) - np.array(t_rect[2]))
pt21 = line2.interpolate(length * (1.0 - gsr))
pt22 = line2.interpolate(length * gsr)
line4 = sgls.LineString((t_rect[3], t_rect[0]))
length = npla.norm(np.array(t_rect[3]) - np.array(t_rect[0]))
pt42 = line4.interpolate(length * (1.0 - gsr))
pt41 = line4.interpolate(length * gsr)
gsLines = []
gsLines.append(sgls.LineString((pt11, pt31)))
gsLines.append(sgls.LineString((pt12, pt32)))
gsLines.append(sgls.LineString((pt21, pt41)))
gsLines.append(sgls.LineString((pt22, pt42)))
dObjGs = []
for i in range(len(objPos)):
dd = []
for j in range(len(gsLines)):
dd.append(gsLines[j].distance(spt.Point(objPos[i])))
dObjGs.append(min(dd))
gP = np.sum(dObjGs)
gP /= (1.0 * dR * len(objPos))
return gP
?文章來源地址http://www.zghlxwxcb.cn/news/detail-861506.html
2.10 成本函數(shù)(Cost Function)
空間布局優(yōu)化的成本函數(shù)定義為上述定義項的加權(quán)和:
每個指南的權(quán)重是在我們的實驗中根據(jù)經(jīng)驗設(shè)定的,對大多數(shù)權(quán)重使用值1.0,流通(Circulation)權(quán)重設(shè)為1.1,占比(Proportion)權(quán)重設(shè)為2.5。增加了占比權(quán)重是因為此規(guī)則對于將組件插入場景至關(guān)重要。另外我們把功能需求(Functional Needs)權(quán)重設(shè)置到 3.0,因為這條規(guī)則對于向特定空間添加合適的組件非常重要。最后將黃金分割(Golden Section)權(quán)重設(shè)置為0.5。我們發(fā)現(xiàn)這些權(quán)重最適合我們的系統(tǒng),并且我們在所有實驗中都使用了它們。
# 計算方法
def Aligment_transformation(self , objects_list , dR_room):
all_objects_front_center_points = []
all_objects_back_center_points = []
for item in objects_list:
x1 =int(item["point"].x)
y1 = int(item["point"].y)
x2 = x1 + int(item["furniture"].top_right.x)
y2 = y1 + int(item["furniture"].top_right.y)
if (self.rotation == 0):
front = self.midpoint(Point(x1,y1),Point(x2,y1))
back = self.midpoint(Point(x1,y2),Point(x2,y2))
elif (self.rotation == 1):
front = self.midpoint(Point(x1,y2),Point(x1,y1))
back = self.midpoint(Point(x2,y2),Point(x2,y1))
elif (self.rotation == 2):
front = self.midpoint(Point(x2,y2),Point(x1,y2))
back = self.midpoint(Point(x2,y1),Point(x1,y1))
elif (self.rotation == 3):
front = self.midpoint(Point(x2,y1),Point(x2,y2))
back = self.midpoint(Point(x1,y1),Point(x1,y2))
all_objects_front_center_points.append(front)
all_objects_back_center_points.append(back)
# all_objects_front_points.append(front)
walls = []
walls.append(((0, 0), (100, 0)))
walls.append(((100, 0), (100, 100)))
walls.append(((100, 100), (0, 100)))
walls.append(((0, 100), (0, 0)))
wallProbVec = np.array([0.2, 0.2, 0.4, 0.6,0.2,0.6])
# dR = 600
return (self.calcAlignment(all_objects_back_center_points,walls,wallProbVec,dR_room))
def group_relationship_transformation(self ,objects_list ,roomPoints , dR_room):
"""
Transformation function for group_relationship and rythm and GoldenSec
"""
all_items_center = []
furniture_type = []
for idx,item in enumerate(objects_list):
centr_point = baseParent.center(self , idx)
all_items_center.append(geometry.Point(centr_point.x,centr_point.y))
furniture_type.append(item["furniture"].room_type)
# roomPoints= [geometry.Point(0,0),geometry.Point(100,0)
# ,geometry.Point(100,100),geometry.Point(0,100)]
# dR_room = np.sqrt(100**2+100**2) #Diagonal Size of the room
group_Relation = self.calcGroupRelation(all_items_center,furniture_type,dR_room)
objectDistribution = self.calcObjDistrib(all_items_center)
goldenSection = self.calcGoldenSec(all_items_center,roomPoints,dR_room)*0.5
return(group_Relation + objectDistribution + goldenSection)
def circulation_transformation (self ,objects_list):
"""
Transforms
"""
all_objects_points = []
sp_list = []
tp_list = []
for item in objects_list:
current_object_points = []
x1 =int(item["point"].x)
y1 = int(item["point"].y)
x2 = x1 + int(item["furniture"].top_right.x)
y2 = y1 + int(item["furniture"].top_right.y)
p1 = geometry.Point(x1,y1)
p2 = geometry.Point(x2,y1)
p3 = geometry.Point(x2,y2)
p4 = geometry.Point(x1,y2)
sp = geometry.Point(0,0)
tp = p1
current_object_points = [p1,p2,p3,p4]
all_objects_points.append(current_object_points)
sp_list.append(sp)
tp_list.append(tp)
cost = self.calcLayoutCirculation(all_objects_points,sp_list,tp_list)*1.1
return(cost)
def clearnace_transformation (self , objects_list,roomPoints):
"""
Transformation function for clearance and proportion
"""
all_objects_points = []
for item in objects_list:
current_object_points = []
x1 =int(item["point"].x)
y1 = int(item["point"].y)
x2 = x1 + int(item["furniture"].top_right.x)
y2 = y1 + int(item["furniture"].top_right.y)
p1 = geometry.Point(x1,y1)
p2 = geometry.Point(x2,y1)
p3 = geometry.Point(x2,y2)
p4 = geometry.Point(x1,y2)
current_object_points = [p1,p2,p3,p4]
all_objects_points.append(current_object_points)
clearance = self.calcLayoutClearance(all_objects_points)
# roomPoints= [geometry.Point(0,0),geometry.Point(100,0)
# ,geometry.Point(100,100),geometry.Point(0,100)]
proportion = self.calcProportion(all_objects_points,roomPoints)*2.5
return (clearance + proportion)
def cost_function(self , objects_list ,roomPoints ,dR_room):
clearance_proportion=self.clearnace_transformation(objects_list,roomPoints)
circulation = self.circulation_transformation(objects_list)
groupRelationship_rythm_goldSec = self.group_relationship_transformation(objects_list,roomPoints,
dR_room )
alignment = self.Aligment_transformation(objects_list , dR_room)
actual_amounts = {'Table':2,'Chair':2,'Bed':2}
desired_amounts = {'Table':1,'Chair':4,'Bed':1}
Functionality = self.calcFunctionality(self.object_importance,actual_amounts
,desired_amounts)*3
result = clearance_proportion + circulation + groupRelationship_rythm_goldSec + Functionality + alignment
return(result)
3.程序裝飾
裝飾元素和日常使用的小物品使室內(nèi)空間感覺更加舒適和有人居住。因此我們通過使用小對象進行程序裝飾來增強自動家具布局的方法。程序裝飾局部調(diào)用將裝飾物布置到房間的程序,由我們貪婪的成本最小化提供。這些過程通常根據(jù)給定的定位規(guī)則將物體定位到支撐表面上(例如辦公桌上的筆記本電腦)。我們還允許在給定的空間和角度維度范圍內(nèi)隨機放置對象。每個裝飾過程都分配了一組對象,可以通過為此過程給出的規(guī)則來定位這些對象。在將對象放入場景之前,會評估與現(xiàn)有對象的碰撞,如果發(fā)生碰撞,則不會插入該對象。我們將以下程序集成到我們的系統(tǒng)中:
3.1 窗戶裝飾
通常房間的重建幾何體包含窗戶的開口,但這些開口中沒有插入窗戶模型。因此我們的程序從數(shù)據(jù)庫中選擇最適合窗戶開口縱橫比的窗戶模型,以盡量減少窗戶模型的變形。最后縮放窗戶模型以適合開口并正確定位。
窗簾:經(jīng)常用作室內(nèi)空間的裝飾元素。因此如果周圍的墻壁和物體允許,我們的程序裝飾會將窗簾放置在窗戶前面。窗簾按比例縮放以適應(yīng)房間的高度并圍繞窗戶空間。在實驗中我們縮放了窗簾的寬度以覆蓋窗戶寬度的140%。
百葉窗:可用于控制室內(nèi)空間的光量,因此通常是內(nèi)部或外部的一部分。百葉窗定位程序首先選擇數(shù)據(jù)庫中最適合窗戶開口的 3D 模型,然后對其進行縮放并定位在窗戶前面。
3.2 門裝飾
此過程將門對象定位到房間幾何形狀的門洞中,選擇合適的門型號及其位置的過程與窗戶的過程相同,不同之處在于它使用不同的一組 3D 模型進行定位。
3.3 桌子裝飾
室內(nèi)空間的桌子有不同的用途,它們總是用作日常使用元素的支撐面,例如餐具、鮮花或辦公設(shè)備。由于不同房間的桌子用途不同,我們的程序根據(jù)使用它們的房間來裝飾桌子。我們實現(xiàn)了廚房、客廳和辦公室的桌子裝飾。每個房間都有其特定的一組對象來裝飾桌子。餐桌裝飾利用桌椅的親子關(guān)系,增加盤子和餐具位于每把椅子前面的桌子上。對于辦公室和客廳的桌子,物體隨機定位,更有可能位于桌子的中央。辦公桌上物體的旋轉(zhuǎn)是以每個物體的前向量指向椅子的方式計算的。該方向隨機擾動,允許的偏航角變化為 15 度。最后,如果桌子周圍有足夠的空間可以插入地毯,則廚房和客廳的桌子下面的地板可以用地毯裝飾,地毯模型也是從數(shù)據(jù)庫中隨機選擇的。
3.4 轉(zhuǎn)角裝飾
在室內(nèi)設(shè)計中,空的角落可以用植物、燈具或其他物體來裝飾。因此我們將角落裝飾的程序集成到我們的系統(tǒng)中。算法首先找到由墻壁和家具形成的角點。然后,將來自特定 3D 模型集的隨機裝飾對象放置到這些角落,直到放置的裝飾對象的數(shù)量達到所需數(shù)量。在我們的實驗中使用經(jīng)驗設(shè)置的數(shù)字 3 作為角裝飾對象的所需數(shù)量。
3.5 水平表面的裝飾
有人居住的室內(nèi)空間傾向于使用水平表面(例如架子或柜子的頂桌)來存放裝飾物或日常用品(例如書籍、花瓶、蠟燭)。因此我們集成了將裝飾物定位到空的水平表面上的程序。實施中我們使用櫥柜和架子作為基礎(chǔ)水平表面,裝飾程序從數(shù)據(jù)庫中隨機選擇一個裝飾對象并將其放置在基面上的隨機位置上,此放置使用對象的邊界框來防止對象覆蓋在基礎(chǔ)表面的邊緣上。
我們的程序裝飾涵蓋了放置小物件和裝飾物的最常見空間。然而可以通過向系統(tǒng)中添加新程序來擴展程序裝飾。每個新程序都應(yīng)該使用自己的一組裝飾對象以及自己的規(guī)則來相對于現(xiàn)有的家具配置來定位對象。我們在基于 C++ 的虛幻引擎 4 中實現(xiàn)了我們的算法。裝飾程序是用C++語言實現(xiàn)的。沒有和有程序裝飾的帶家具房間的比較如下圖所示。
沒有(左)和有(右)程序裝飾的最終家具布置。從上到下顯示的場景為:臥室、廚房、客廳、辦公室
三、材料優(yōu)化
通過引入表面類別并為屬于特定類別的所有表面選擇一種通用材料來實現(xiàn)材料的一致性。在實驗中使用以下類別的表面:織物、木材、玻璃、鉻、金屬、塑料、陶瓷和石頭。通過使用材料數(shù)據(jù)庫將一種材料隨機分配給每個表面類別。
此外使用來自導(dǎo)入幾何模型的材料名稱來識別特定類別的表面,即如果類別名稱與此表面上的部分材料名稱匹配,則該表面被分配給一個類別。此方法對具有相同類別的表面進行分組,然后通過針對數(shù)據(jù)驅(qū)動的成本函數(shù)優(yōu)化每個類別的材料分配來實現(xiàn)房間內(nèi)材料的顏色和諧。我們的成本函數(shù)模擬場景中存在的顏色之間的顏色兼容性,使用貪心算法來最小化這個成本函數(shù)。在每次優(yōu)化迭代中執(zhí)行以下步驟:
-
為每個類別隨機選擇一種材料并將其分配給該類別的表面
-
從與房間角落對齊的四個視點渲染場景
-
從渲染圖像中提取 5 種主色
-
通過計算提取的 5 色調(diào)色板的成本來評估場景中的顏色兼容性
-
如果新分配的材料的成本函數(shù)低于之前分配的成本函數(shù),則接受這個新的材料配置
顏色兼容性
材質(zhì)的優(yōu)化使用顏色兼容性來為場景中的每種材質(zhì)配置分配成本。使用由 5 種顏色組成的調(diào)色板來表示場景中的主色。調(diào)色板中的顏色在 CIELab 顏色空間中表示,通過 k-means 聚類從場景的渲染圖像中提取調(diào)色板。然后使用和諧調(diào)色板數(shù)據(jù)庫來計算提取調(diào)色板的成本。帶有提取調(diào)色板的室內(nèi)設(shè)計場景如下圖所示:
?
頂部:我們的系統(tǒng)優(yōu)化的室內(nèi)設(shè)計布局和材料。底部:提取的 5 調(diào)色板。
提取的調(diào)色板????的成本是根據(jù)我們的和諧顏色數(shù)據(jù)庫中k最近調(diào)色板的加權(quán)距離計算的:
???? 是與提取的調(diào)色板 ???? 第 i 個最接近的調(diào)色板,???? 是調(diào)色板 ???? 的評級。我們數(shù)據(jù)庫中的評級已標準化。我們在實驗中使用 ?? = 10。調(diào)色板的距離 |???? ? ????|計算為 CIELab 顏色空間中調(diào)色板中各個顏色之間的距離之和:
?
其中 ?????? 和 ?????? 是相應(yīng)調(diào)色板中的第 c 個顏色。距離 |?????? ? ??????|是 CIELab 色彩空間中的歐幾里德距離。所有調(diào)色板中的顏色按 L 值排序,以計算相應(yīng)顏色之間的距離。
顏色兼容性數(shù)據(jù)庫
我們的和諧色彩數(shù)據(jù)庫包含 100000 個帶有指定等級的調(diào)色板。該數(shù)據(jù)庫是通過使用藝術(shù)家創(chuàng)建的和諧調(diào)色板形成的,這些調(diào)色板從在線資源??www.colourloves.com??獲得。我們從 Colourloves 網(wǎng)站下載了 500000 個帶有用戶評分的調(diào)色板。這些調(diào)色板包含一般設(shè)計任務(wù)中使用的顏色,包括網(wǎng)頁設(shè)計、建筑設(shè)計、室內(nèi)設(shè)計等。我們使用基于圖像的方法來選擇適合室內(nèi)設(shè)計的調(diào)色板。我們從互聯(lián)網(wǎng)上下載了 10000 張室內(nèi)設(shè)計圖像,并通過 k 均值聚類提取了它們的主要調(diào)色板。然后,每個調(diào)色板的室內(nèi)設(shè)計適宜性評級。Colourlovers 計算為從提取的室內(nèi)設(shè)計調(diào)色板中到 k 最近調(diào)色板的平均距離。提取的調(diào)色板被添加到下載的調(diào)色板中,形成和諧色彩的初始數(shù)據(jù)庫。該數(shù)據(jù)庫中的每個調(diào)色板都被分配了一個新的評級????,計算公式為???? = 0.6???? + 0.4????,其中????是從室內(nèi)設(shè)計圖像中提取的評級,????是來自Colourlovers數(shù)據(jù)庫的用戶評級。兩個評級首先通過最大值進行歸一化。權(quán)重 0.6 和 0.4 是根據(jù)經(jīng)驗設(shè)置的。從圖像中提取的調(diào)色板的用戶評級 ???? 為 1.0。
最后,我們通過選擇 100000 個具有最佳評級????的調(diào)色板形成了我們的和諧調(diào)色板數(shù)據(jù)庫。該數(shù)據(jù)庫用于我們的貪婪材質(zhì)優(yōu)化,以選擇具有和諧顏色的材質(zhì)配置。我們的材料優(yōu)化前后的室內(nèi)設(shè)計結(jié)果可以在補充文件中看到。此外,我們的系統(tǒng)不僅能夠優(yōu)化家具材料,還能夠優(yōu)化墻壁和地板的材料。
四、相關(guān)資料
1.基于遺傳算法的室內(nèi)自動布局
-
論文:https://publik.tuwien.ac.at/files/publik_262718.pdf
-
源碼:見上傳附件文章來源:http://www.zghlxwxcb.cn/news/detail-861506.html
2.基于成本最小化貪婪算法室內(nèi)自動布局
-
論文:http://www.peterkan.com/download/ieeevr2018.pdf
-
源碼:見上傳附件
?
到了這里,關(guān)于苦于實現(xiàn)3D空間自動布局的同學(xué),請參考:3D室內(nèi)空間布局自動化算法分析(含源碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!