add LayoutGMN apply - gds prser and graph constructor
This commit is contained in:
@@ -39,30 +39,76 @@ class GDSParser:
|
||||
return patches
|
||||
|
||||
def extract_geometries_from_patch(self, patch_bbox: Tuple[float, float, float, float]) -> List[Dict]:
|
||||
"""从给定的区块中提取所有几何对象。
|
||||
"""从给定的区块中提取所有几何对象,并记录全局与区块内(裁剪后)的信息。
|
||||
|
||||
说明:
|
||||
- 为了处理跨越多个区块的多边形,本函数会计算多边形与区块边界框的布尔相交,
|
||||
得到位于该区块内的裁剪多边形,并同时记录原始(全局)多边形信息。
|
||||
|
||||
Args:
|
||||
patch_bbox: 区块的边界框 (x_min, y_min, x_max, y_max)。
|
||||
|
||||
Returns:
|
||||
一个字典列表,每个字典代表一个几何对象及其属性(多边形、层、边界框)。
|
||||
一个字典列表,每个字典代表一个几何对象及其属性:
|
||||
- global_points: 原始多边形顶点(Nx2 ndarray)
|
||||
- global_bbox: 原始多边形边界框
|
||||
- global_area: 原始多边形面积
|
||||
- clipped_points: 与区块相交后的裁剪多边形顶点(Mx2 ndarray,可能为空)
|
||||
- clipped_area: 裁剪后面积(可能为 0)
|
||||
- area_ratio: 裁剪面积 / 原始面积(用于衡量跨区块比例)
|
||||
- is_partial: 是否为跨区块(裁剪面积 < 原始面积)
|
||||
- layer: 层映射到的整数索引
|
||||
- patch_bbox: 当前区块边界框
|
||||
"""
|
||||
x_min, y_min, x_max, y_max = patch_bbox
|
||||
# 获取单元内的所有多边形
|
||||
rect = gdstk.rectangle(x_min, y_min, x_max, y_max)
|
||||
polygons = self.top_cell.get_polygons(by_spec=True)
|
||||
geometries = []
|
||||
# 遍历所有多边形
|
||||
geometries: List[Dict] = []
|
||||
|
||||
for (layer, datatype), poly_list in polygons.items():
|
||||
layer_str = f"{layer}/{datatype}"
|
||||
# 只处理在 layer_mapping 中定义的层
|
||||
if layer_str in self.layer_mapping:
|
||||
for poly in poly_list:
|
||||
# 简单的边界框相交检查
|
||||
p_xmin, p_ymin, p_xmax, p_ymax = poly.bb()
|
||||
if not (p_xmax < x_min or p_xmin > x_max or p_ymax < y_min or p_ymin > y_max):
|
||||
geometries.append({
|
||||
"polygon": poly,
|
||||
"layer": self.layer_mapping[layer_str],
|
||||
"bbox": (p_xmin, p_ymin, p_xmax, p_ymax)
|
||||
})
|
||||
if layer_str not in self.layer_mapping:
|
||||
continue
|
||||
layer_idx = self.layer_mapping[layer_str]
|
||||
|
||||
for poly in poly_list:
|
||||
p_xmin, p_ymin, p_xmax, p_ymax = poly.bb()
|
||||
# 快速边界框测试(若无相交则跳过)
|
||||
if p_xmax < x_min or p_xmin > x_max or p_ymax < y_min or p_ymin > y_max:
|
||||
continue
|
||||
|
||||
# 全局多边形点与面积
|
||||
global_points = np.array(poly.points, dtype=float)
|
||||
global_area = abs(gdstk.Polygon(global_points).area())
|
||||
|
||||
# 与区块矩形做相交,可能返回多个多边形
|
||||
clipped = gdstk.boolean([poly], [rect], "and", precision=1e-3, layer=layer, datatype=datatype)
|
||||
clipped_points_list: List[np.ndarray] = []
|
||||
clipped_area = 0.0
|
||||
if clipped:
|
||||
for cpoly in clipped:
|
||||
pts = np.array(cpoly.points, dtype=float)
|
||||
if pts.size == 0:
|
||||
continue
|
||||
area = abs(gdstk.Polygon(pts).area())
|
||||
if area <= 0:
|
||||
continue
|
||||
clipped_points_list.append(pts)
|
||||
clipped_area += area
|
||||
|
||||
area_ratio = (clipped_area / global_area) if global_area > 0 else 0.0
|
||||
is_partial = area_ratio < 0.999 # 允许微小数值误差
|
||||
|
||||
geometries.append({
|
||||
"global_points": global_points,
|
||||
"global_bbox": (p_xmin, p_ymin, p_xmax, p_ymax),
|
||||
"global_area": float(global_area),
|
||||
"clipped_points_list": clipped_points_list, # 可能包含多个裁剪片段
|
||||
"clipped_area": float(clipped_area),
|
||||
"area_ratio": float(area_ratio),
|
||||
"is_partial": bool(is_partial),
|
||||
"layer": layer_idx,
|
||||
"patch_bbox": (x_min, y_min, x_max, y_max),
|
||||
})
|
||||
|
||||
return geometries
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import List, Dict
|
||||
from typing import List, Dict, Tuple
|
||||
import torch
|
||||
from torch_geometric.data import Data
|
||||
from scipy.spatial import cKDTree
|
||||
@@ -32,21 +32,64 @@ class GraphConstructor:
|
||||
if not geometries:
|
||||
return None
|
||||
|
||||
node_features = []
|
||||
node_positions = []
|
||||
# 提取每个几何图形的特征
|
||||
for geo in geometries:
|
||||
x_min, y_min, x_max, y_max = geo["bbox"]
|
||||
width = x_max - x_min
|
||||
height = y_max - y_min
|
||||
area = width * height
|
||||
centroid_x = x_min + width / 2
|
||||
centroid_y = y_min + height / 2
|
||||
node_features: List[List[float]] = []
|
||||
node_positions: List[List[float]] = []
|
||||
node_layers: List[int] = []
|
||||
node_meta: List[Dict] = []
|
||||
|
||||
# 提取每个几何图形的特征(优先使用裁剪后片段的质心;若无裁剪片段,则使用全局质心)
|
||||
for geo in geometries:
|
||||
layer_idx: int = int(geo["layer"]) if "layer" in geo else 0
|
||||
global_bbox = geo.get("global_bbox", None)
|
||||
global_points = geo.get("global_points", None)
|
||||
clipped_points_list = geo.get("clipped_points_list", []) or []
|
||||
clipped_area = float(geo.get("clipped_area", 0.0))
|
||||
global_area = float(geo.get("global_area", 0.0))
|
||||
area_ratio = float(geo.get("area_ratio", 0.0))
|
||||
is_partial = bool(geo.get("is_partial", False))
|
||||
|
||||
# 选择用于节点位置与宽高的几何:若存在裁剪片段,聚合其外接框,否则用全局框
|
||||
if clipped_points_list:
|
||||
# 合并所有裁剪片段点,计算整体外接框与质心
|
||||
all_pts = np.vstack(clipped_points_list)
|
||||
elif global_points is not None:
|
||||
all_pts = np.array(global_points, dtype=float)
|
||||
else:
|
||||
# 回退到 bbox 信息(兼容旧格式)
|
||||
x_min, y_min, x_max, y_max = geo["bbox"]
|
||||
all_pts = np.array([[x_min, y_min], [x_max, y_max]], dtype=float)
|
||||
|
||||
x_min, y_min = np.min(all_pts, axis=0)
|
||||
x_max, y_max = np.max(all_pts, axis=0)
|
||||
width = float(x_max - x_min)
|
||||
height = float(y_max - y_min)
|
||||
centroid_x = float(x_min + width / 2.0)
|
||||
centroid_y = float(y_min + height / 2.0)
|
||||
|
||||
# 节点特征:质心、宽、高、裁剪面积、全局面积占比、层索引(数值化)
|
||||
features = [
|
||||
centroid_x,
|
||||
centroid_y,
|
||||
width,
|
||||
height,
|
||||
clipped_area,
|
||||
(clipped_area / global_area) if global_area > 0 else 0.0,
|
||||
float(layer_idx),
|
||||
1.0 if is_partial else 0.0,
|
||||
]
|
||||
|
||||
# 特征包括:中心点坐标、宽度、高度、面积
|
||||
features = [centroid_x, centroid_y, width, height, area]
|
||||
node_features.append(features)
|
||||
node_positions.append([centroid_x, centroid_y])
|
||||
node_layers.append(layer_idx)
|
||||
# 将原始与裁剪的必要元信息保存在 Data 中(以便后续可视化与调试)
|
||||
node_meta.append({
|
||||
"layer": layer_idx,
|
||||
"global_bbox": tuple(global_bbox) if global_bbox is not None else None,
|
||||
"global_area": global_area,
|
||||
"clipped_area": clipped_area,
|
||||
"area_ratio": area_ratio,
|
||||
"is_partial": is_partial,
|
||||
})
|
||||
|
||||
# 将特征和位置转换为 PyTorch 张量
|
||||
x = torch.tensor(node_features, dtype=torch.float)
|
||||
@@ -57,6 +100,9 @@ class GraphConstructor:
|
||||
|
||||
# 创建图数据对象
|
||||
data = Data(x=x, edge_index=edge_index, pos=pos, y=torch.tensor([label], dtype=torch.float))
|
||||
# 附加层索引与元信息(元信息以对象列表形式保存,供上层使用;不会参与张量运算)
|
||||
data.layer = torch.tensor(node_layers, dtype=torch.long)
|
||||
data.node_meta = node_meta
|
||||
return data
|
||||
|
||||
def _create_edges(self, node_positions: torch.Tensor) -> torch.Tensor:
|
||||
|
||||
Reference in New Issue
Block a user