### Quick Start: Train and Test Model Source: https://github.com/nicehuster/sympoint/blob/main/README.md These bash commands provide a quick start guide for training and testing the SymPoint model using distributed processing. They are the primary scripts to initiate the model's learning and evaluation phases. ```bash #train bash tools/train_dist.sh #test bash tools/test_dist.sh ``` -------------------------------- ### Install Environment and Dependencies Source: https://github.com/nicehuster/sympoint/blob/main/README.md This snippet details the commands to create a conda environment and install necessary Python packages, including PyTorch, Detectron2, and custom pointops module. It ensures the correct versions are installed for compatibility. ```bash conda create -n spv1 python=3.8 -y conda activate spv1 pip install torch==1.10.0+cu111 torchvision==0.11.0+cu111 -f https://download.pytorch.org/whl/torch_stable.html pip install gdown mmcv==0.2.14 svgpathtools==1.6.1 munch==2.5.0 tensorboard==2.12.0 tensorboardx==2.5.1 detectron2==0.6 python -m pip install 'git+https://github.com/facebookresearch/detectron2.git' # compile pointops cd modules/pointops python setup.py install ``` -------------------------------- ### SVGDataset Class for PyTorch Source: https://context7.com/nicehuster/sympoint/llms.txt The SVGDataset class is a PyTorch Dataset implementation designed to load preprocessed JSON files. It is intended for use during model training and supports data augmentation. This class requires PyTorch to be installed. ```python import torch from torch.utils.data import Dataset import json class SVGDataset(Dataset): def __init__(self, json_dir, transform=None): self.json_dir = json_dir self.transform = transform self.file_list = [f for f in os.listdir(json_dir) if f.endswith('.json')] def __len__(self): return len(self.file_list) def __getitem__(self, idx): json_path = os.path.join(self.json_dir, self.file_list[idx]) with open(json_path, 'r') as f: data = json.load(f) # Assuming 'data' is a dictionary that needs further processing or augmentation # Example: Convert relevant parts to tensors if needed # Example: Apply transformations if self.transform: data = self.transform(data) return data ``` -------------------------------- ### Main Training Script Execution Source: https://context7.com/nicehuster/sympoint/llms.txt Orchestrates the entire training process. It loads configuration, initializes distributed training if specified, sets up logging, builds the model, data loaders, optimizer, and scaler, and then runs the training and validation loops. ```python def main(): args = get_args() cfg = Munch.fromDict(yaml.safe_load(open(args.config))) if args.dist: rank = init_dist() set_seed(args.seed) # Setup logging cfg.work_dir = args.work_dir or f"./work_dirs/{args.exp_name}" os.makedirs(cfg.work_dir, exist_ok=True) logger = get_root_logger(log_file=f"{cfg.work_dir}/train.log") writer = SummaryWriter(cfg.work_dir) # Build model matcher = HungarianMatcher(**cfg.matcher) weight_dict = {"loss_ce": cfg.matcher.cost_class, "loss_mask": cfg.matcher.cost_mask, "loss_dice": cfg.matcher.cost_dice} criterion = SetCriterion(matcher, weight_dict, cfg.criterion).cuda() model = SVGNet(cfg.model, criterion=criterion).cuda() if args.sync_bn: model = nn.SyncBatchNorm.convert_sync_batchnorm(model) if args.dist: model = DistributedDataParallel(model, device_ids=[torch.cuda.current_device()]) # Build data loaders train_set = build_dataset(cfg.data.train, logger) val_set = build_dataset(cfg.data.test, logger) train_loader = build_dataloader(args, train_set, training=True, dist=args.dist, **cfg.dataloader.train) val_loader = build_dataloader(args, val_set, training=False, dist=True, **cfg.dataloader.test) # Optimizer with scaled learning rate optimizer = torch.optim.AdamW(model.parameters(), lr=cfg.optimizer.lr, weight_decay=cfg.optimizer.weight_decay) scaler = torch.cuda.amp.GradScaler(enabled=cfg.fp16) # Training loop best_metric = 0 for epoch in range(1, cfg.epochs + 1): train(epoch, model, optimizer, scaler, train_loader, cfg, logger, writer) sPQ = validate(epoch, model, val_loader, cfg, logger, writer) if sPQ > best_metric: best_metric = sPQ checkpoint_save(epoch, model, optimizer, cfg.work_dir, cfg.save_freq, best=True) logger.info(f"New best sPQ: {best_metric:.3f}") if __name__ == "__main__": main() ``` -------------------------------- ### Download and Prepare FloorPlanCAD Dataset Source: https://context7.com/nicehuster/sympoint/llms.txt This script automates the retrieval of the FloorPlanCAD dataset from Google Drive. It downloads zip archives for training, validation, and test splits, then extracts them into a structured local directory. ```python import os import gdown train_url = "16McNNY_-Y2uVnq42ntZTdYKPWgOZxwp3" val_url = "1xgLqcj91i13_3vhfsUYcRYh3PhFYB9LJ" test_url = "1Hc4-ggsUMoB_5uqJdqYRn9K73QS8rOgG" data_save_dir = "./dataset" os.makedirs(data_save_dir, exist_ok=True) for split, url_id in [("train", train_url), ("val", val_url), ("test", test_url)]: url = f"https://drive.google.com/uc?id={url_id}" zip_path = f"{data_save_dir}/{split}.zip" gdown.download(url, zip_path) unzip_dir = os.path.join(data_save_dir, split) os.makedirs(unzip_dir, exist_ok=True) os.system(f"unzip {zip_path} -d {unzip_dir}") ``` -------------------------------- ### Launch Distributed Training and Evaluation Source: https://context7.com/nicehuster/sympoint/llms.txt Shell scripts to execute distributed training and evaluation using torchrun. These scripts configure the environment, set the number of GPUs, and define the master port for communication. ```bash #!/usr/bin/env bash export PYTHONPATH=./ GPUS=8 OMP_NUM_THREADS=$GPUS torchrun --nproc_per_node=$GPUS --master_port=$((RANDOM + 10000)) tools/train.py --dist configs/svg/svg_pointT.yaml --exp_name baseline_nclsw_grelu --sync_bn ``` ```bash #!/usr/bin/env bash export PYTHONPATH=./ GPUS=8 workdir=./work_dirs/svg/svg_pointT/baseline_nclsw_grelu OMP_NUM_THREADS=$GPUS torchrun --nproc_per_node=$GPUS --master_port=$((RANDOM + 10000)) tools/test.py $workdir/svg_pointT.yaml $workdir/best.pth --dist ``` -------------------------------- ### Full Training Pipeline (Python) Source: https://context7.com/nicehuster/sympoint/llms.txt Implements a comprehensive training pipeline for machine learning models. It supports distributed training, mixed precision, and includes validation steps. ```Python ```python ``` -------------------------------- ### Log Performance Metrics (Python) Source: https://context7.com/nicehuster/sympoint/llms.txt Logs the calculated performance metrics (PQ, RQ, SQ) as percentages, formatted to three decimal places. This function returns the calculated percentages. ```Python logger.info(f'PQ / RQ / SQ: {sPQ*100:.3f} / {sRQ*100:.3f} / {sSQ*100:.3f}') return sPQ*100, sRQ*100, sSQ*100 ``` -------------------------------- ### Download and Preprocess Dataset Source: https://github.com/nicehuster/sympoint/blob/main/README.md This Python script section outlines the process for obtaining the dataset and converting its SVG ground truth data into a format suitable for training and testing the SymPoint model. It includes commands for downloading and parsing different splits (train, val, test). ```python # download dataset python download_data.py # preprocess #train, val, test python parse_svg.py --split train --data_dir ./dataset/train/train/svg_gt/ python parse_svg.py --split val --data_dir ./dataset/val/val/svg_gt/ python parse_svg.py --split test --data_dir ./dataset/test/test/svg_gt/ ``` -------------------------------- ### Prepare Training Targets Source: https://context7.com/nicehuster/sympoint/llms.txt Converts raw point-level semantic and instance labels into a structured format suitable for training the model, filtering out background classes. ```python def prepare_targets(self, semantic_labels, bg_sem=35): instance_ids = semantic_labels[:, 1].cpu().numpy() semantic_ids = semantic_labels[:, 0].cpu().numpy() # ... logic to create binary masks for each instance ... return [{"labels": cls_targets.to(semantic_labels.device), "masks": mask_targets.to(semantic_labels.device)}] ``` -------------------------------- ### Parse Training Arguments Source: https://context7.com/nicehuster/sympoint/llms.txt Parses command-line arguments for training configuration, including config file path, distributed training flags, resume checkpoint, and working directory. ```python def get_args(): parser = argparse.ArgumentParser("svgnet") parser.add_argument("config", type=str, help="path to config file") parser.add_argument("--dist", action="store_true", help="distributed training") parser.add_argument("--sync_bn", action="store_true", help="sync batch norm") parser.add_argument("--resume", type=str, help="path to checkpoint") parser.add_argument("--work_dir", type=str, help="output directory") parser.add_argument("--exp_name", type=str, default="default") parser.add_argument("--seed", type=int, default=2000) parser.add_argument("--local_rank", type=int, default=0) return parser.parse_args() ``` -------------------------------- ### InstanceEval: Panoptic Segmentation Evaluation (Python) Source: https://context7.com/nicehuster/sympoint/llms.txt Implements panoptic segmentation evaluation metrics including Panoptic Quality (PQ), Recognition Quality (RQ), and Segmentation Quality (SQ). It processes instance and target data, calculates Intersection over Union (IoU) for matched predictions, and aggregates TP, FP, and FN counts. Supports distributed evaluation by gathering metrics from multiple GPUs. ```Python import numpy as np import torch import torch.distributed as dist class InstanceEval: """Panoptic segmentation evaluation with PQ, RQ, SQ.""" def __init__(self, num_classes=35, ignore_label=35, gpu_num=8): self._num_classes = num_classes self.gpu_num = gpu_num self.min_obj_score = 0.1 self.IoU_thres = 0.5 self.tp_classes = np.zeros(num_classes) self.tp_classes_values = np.zeros(num_classes) self.fp_classes = np.zeros(num_classes) self.fn_classes = np.zeros(num_classes) self.thing_class = list(range(30)) # Countable objects self.stuff_class = [30, 31, 32, 33, 34] # Uncountable regions def update(self, instances, target, lengths): """Update TP/FP/FN counts for each class.""" lengths = np.round(np.log(1 + lengths.cpu().numpy()), 3) tgt_labels = target["labels"].cpu().numpy().tolist() tgt_masks = target["masks"].transpose(0, 1).cpu().numpy() for tgt_label, tgt_mask in zip(tgt_labels, tgt_masks): if tgt_label == self.ignore_label: continue matched = False for instance in instances: src_label = instance["labels"] src_score = instance["scores"] if src_score < self.min_obj_score: continue src_mask = instance["masks"] interArea = sum(lengths[np.logical_and(src_mask, tgt_mask)]) unionArea = sum(lengths[np.logical_or(src_mask, tgt_mask)]) iou = interArea / (unionArea + 1e-6) if iou >= self.IoU_thres: matched = True if tgt_label == src_label: self.tp_classes[tgt_label] += 1 self.tp_classes_values[tgt_label] += iou else: self.fp_classes[src_label] += 1 if not matched: self.fn_classes[tgt_label] += 1 def get_eval(self, logger): """Compute and log PQ, RQ, SQ metrics.""" # Gather from all GPUs if distributed if self.gpu_num > 1: _tensor = np.stack([self.tp_classes, self.tp_classes_values, self.fp_classes, self.fn_classes]) _tensor = torch.from_numpy(_tensor).to("cuda") _tensor_list = [torch.full_like(_tensor, 0) for _ in range(self.gpu_num)] dist.barrier() dist.all_gather(_tensor_list, _tensor) all_tensor = sum(_tensor_list).cpu().numpy() self.tp_classes, self.tp_classes_values, self.fp_classes, self.fn_classes = all_tensor # Recognition Quality (RQ) = TP / (TP + 0.5*FP + 0.5*FN) # Segmentation Quality (SQ) = sum(IoU) / TP # Panoptic Quality (PQ) = RQ * SQ sRQ = sum(self.tp_classes) / (sum(self.tp_classes) + 0.5*sum(self.fp_classes) + 0.5*sum(self.fn_classes) + 1e-6) sSQ = sum(self.tp_classes_values) / (sum(self.tp_classes) + 1e-6) sPQ = sRQ * sSQ logger.info(f'PQ / RQ / SQ: {sPQ:.3f} / {sRQ:.3f} / {sSQ:.3f}') return sPQ, sRQ, sSQ ``` -------------------------------- ### Define Model Configuration Source: https://context7.com/nicehuster/sympoint/llms.txt YAML configuration file specifying the model architecture, matcher parameters, loss criteria, data loading, and optimization settings for the SymPoint model. ```yaml model: in_channels: 9 semantic_classes: 35 num_decoders: 3 dropout: 0.0 pre_norm: False num_heads: 8 shared_decoder: True dim_feedforward: 512 hidden_dim: 256 num_queries: 500 gauss_scale: 1.0 normalize_pos_enc: False matcher: cost_class: 2.0 cost_mask: 5.0 cost_dice: 5.0 num_points: -1 criterion: num_classes: 35 eos_coef: 0.1 losses: ["labels", "masks"] ignore_label: -1 data: train: type: 'svg' data_root: 'dataset/train/jsons' repeat: 5 split: "train" data_norm: "mean" aug: aug_prob: 0.5 hflip: True vflip: True rotate2: True scale: {enable: True, ratio: [0.5, 1.5]} shift: {enable: True, scale: [-0.5, 0.5]} cutmix: {enable: True, queueK: 32, relative_shift: [-0.5, 0.5]} test: type: 'svg' data_root: 'dataset/test/jsons' repeat: 1 split: "test" data_norm: "mean" aug: False dataloader: train: {batch_size: 2, num_workers: 2} test: {batch_size: 1, num_workers: 1} optimizer: type: 'AdamW' lr: 0.0001 weight_decay: 0.0001 clip_gradients_value: 0.01 fp16: False epochs: 50 step_epoch: 30 save_freq: 10 ``` -------------------------------- ### Training Loop with Optimizer and Scaler Source: https://context7.com/nicehuster/sympoint/llms.txt Implements the core training loop for a single epoch. It iterates through the training data, performs forward and backward passes, updates model weights using an optimizer and gradient scaler, and logs training progress. ```python def train(epoch, model, optimizer, scaler, train_loader, cfg, logger, writer): model.train() for i, batch in enumerate(train_loader, start=1): cosine_lr_after_step(optimizer, cfg.optimizer.lr, epoch - 1, cfg.step_epoch, cfg.epochs) with torch.cuda.amp.autocast(enabled=cfg.fp16): _, loss, log_vars = model(batch) optimizer.zero_grad() scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() if i % 50 == 0: logger.info(f"Epoch [{epoch}/{cfg.epochs}][{i}/{len(train_loader)}] loss: {loss.item():.4f}") checkpoint_save(epoch, model, optimizer, cfg.work_dir, cfg.save_freq) ``` -------------------------------- ### InstanceEval - Panoptic Segmentation Evaluation Source: https://context7.com/nicehuster/sympoint/llms.txt Evaluates panoptic segmentation performance using metrics like Panoptic Quality (PQ), Recognition Quality (RQ), and Segmentation Quality (SQ). It supports distributed evaluation. ```APIDOC ## InstanceEval Class ### Description Calculates panoptic segmentation evaluation metrics: Panoptic Quality (PQ), Recognition Quality (RQ), and Segmentation Quality (SQ). ### Initialization ```python InstanceEval(num_classes=35, ignore_label=35, gpu_num=8) ``` - **num_classes** (int) - The total number of classes. - **ignore_label** (int) - The label to ignore. - **gpu_num** (int) - The number of GPUs used. ### Parameters - **min_obj_score** (float) - Minimum score threshold for an object instance to be considered. - **IoU_thres** (float) - Intersection over Union threshold for matching predictions to ground truth. - **thing_class** (list) - List of class IDs considered as 'thing' classes (countable objects). - **stuff_class** (list) - List of class IDs considered as 'stuff' classes (uncountable regions). ### Methods #### update(instances, target, lengths) Updates the counts for True Positives (TP), False Positives (FP), and False Negatives (FN) for each class based on instance predictions and ground truth. - **instances** (list) - A list of predicted instance objects, each containing 'labels', 'scores', and 'masks'. - **target** (dict) - A dictionary containing ground truth 'labels' and 'masks'. - **lengths** (torch.Tensor) - Tensor containing lengths related to masks. #### get_eval(logger) Computes and logs PQ, RQ, and SQ metrics. Aggregates counts from all GPUs if distributed. - **logger** (logging.Logger) - A logger object for outputting results. ### Metrics Computed - **PQ**: Panoptic Quality (RQ * SQ). - **RQ**: Recognition Quality. - **SQ**: Segmentation Quality. ### Example Usage ```python eval_metric = InstanceEval(num_classes=35, ignore_label=35, gpu_num=8) # ... during evaluation ... predicted_instances = [...] # list of predicted instances ground_truth = {...} # dictionary with target labels and masks mask_lengths = torch.tensor([...]) eval_metric.update(predicted_instances, ground_truth, mask_lengths) # ... after evaluation ... metrics = eval_metric.get_eval(logger) ``` ``` -------------------------------- ### Hungarian Matcher for Training Source: https://context7.com/nicehuster/sympoint/llms.txt Implements bipartite matching between predictions and ground truth using the Hungarian algorithm. It computes cost matrices for classification, mask, and dice similarities, then uses the Hungarian algorithm to find the optimal assignment. This is crucial for training set prediction models. ```python import torch import torch.nn.functional as F from scipy.optimize import linear_sum_assignment from torch import nn from torch.cuda.amp import autocast def batch_dice_loss(inputs: torch.Tensor, targets: torch.Tensor): """Compute DICE loss between predicted and target masks.""" inputs = inputs.sigmoid().flatten(1) numerator = 2 * torch.einsum("nc,mc->nm", inputs, targets) denominator = inputs.sum(-1)[:, None] + targets.sum(-1)[None, :] loss = 1 - (numerator + 1) / (denominator + 1) return loss def batch_sigmoid_ce_loss(inputs: torch.Tensor, targets: torch.Tensor): """Compute sigmoid cross-entropy loss matrix.""" hw = inputs.shape[1] pos = F.binary_cross_entropy_with_logits(inputs, torch.ones_like(inputs), reduction="none") neg = F.binary_cross_entropy_with_logits(inputs, torch.zeros_like(inputs), reduction="none") loss = torch.einsum("nc,mc->nm", pos, targets) + torch.einsum("nc,mc->nm", neg, (1 - targets)) return loss / hw class HungarianMatcher(nn.Module): """Compute optimal assignment between predictions and ground truth.""" def __init__(self, cost_class: float = 1, cost_mask: float = 1, cost_dice: float = 1, num_points: int = 0): super().__init__() self.cost_class = cost_class self.cost_mask = cost_mask self.cost_dice = cost_dice self.num_points = num_points @torch.no_grad() def forward(self, outputs, targets): """Perform matching for each sample in batch.""" bs, num_queries = outputs["pred_logits"].shape[:2] indices = [] for b in range(bs): out_prob = outputs["pred_logits"][b].softmax(-1) tgt_ids = targets[b]["labels"] out_mask = outputs["pred_masks"][b] tgt_mask = targets[b]["masks"].to(out_mask) with autocast(enabled=False): out_mask = out_mask.float() tgt_mask = tgt_mask.float().transpose(0, 1) # Compute cost matrices cost_mask = batch_sigmoid_ce_loss(out_mask, tgt_mask) cost_dice = batch_dice_loss(out_mask, tgt_mask) # Focal loss for classification alpha, gamma = 0.25, 4.0 iou = (1 - cost_dice) / (1 + cost_dice + 1e-8) _out_prob = out_prob[:, tgt_ids] * iou neg_cost = (1 - alpha) * (_out_prob ** gamma) * (-(1 - _out_prob + 1e-5).log()) pos_cost = alpha * ((1 - _out_prob) ** gamma) * (-(_out_prob + 1e-5).log()) cost_class = pos_cost - neg_cost # Combined cost matrix C = self.cost_mask * cost_mask + self.cost_class * cost_class + self.cost_dice * cost_dice C = C.reshape(num_queries, -1).cpu() # Hungarian algorithm indices.append(linear_sum_assignment(C)) return [(torch.as_tensor(i, dtype=torch.int64), torch.as_tensor(j, dtype=torch.int64)) for i, j in indices] # Usage in training matcher = HungarianMatcher(cost_class=2.0, cost_mask=5.0, cost_dice=5.0) indices = matcher(outputs, targets) # Returns matched pairs for loss computation ``` -------------------------------- ### PointWiseEval: Semantic Segmentation Evaluation (Python) Source: https://context7.com/nicehuster/sympoint/llms.txt Implements semantic segmentation evaluation metrics including mean Intersection over Union (mIoU) and pixel accuracy (pACC). It supports distributed data collection via `torch.distributed` and logs results using a provided logger. Handles ignore labels and computes metrics based on a confusion matrix. ```Python import numpy as np import torch import torch.distributed as dist class PointWiseEval: """Semantic segmentation evaluation with mIoU.""" def __init__(self, num_classes=35, ignore_label=35, gpu_num=1): self.ignore_label = ignore_label self._num_classes = num_classes self._conf_matrix = np.zeros((num_classes + 1, num_classes + 1), dtype=np.float32) self.gpu_num = gpu_num def update(self, pred_sem, gt_sem): """Update confusion matrix with batch predictions.""" pos_inds = gt_sem != self.ignore_label pred = pred_sem[pos_inds] gt = gt_sem[pos_inds] self._conf_matrix += np.bincount( (self._num_classes + 1) * pred.reshape(-1) + gt.reshape(-1), minlength=self._conf_matrix.size, ).reshape(self._conf_matrix.shape) def get_eval(self, logger): """Compute and log mIoU, fwIoU, and pixel accuracy.""" if self.gpu_num > 1: # Gather confusion matrices from all GPUs t = torch.from_numpy(self._conf_matrix).to("cuda") conf_matrix_list = [torch.full_like(t, 0) for _ in range(self.gpu_num)] dist.barrier() dist.all_gather(conf_matrix_list, t) self._conf_matrix = sum(conf_matrix_list).cpu().numpy() tp = self._conf_matrix.diagonal()[:-1].astype(np.float64) pos_gt = np.sum(self._conf_matrix[:-1, :-1], axis=0).astype(np.float64) pos_pred = np.sum(self._conf_matrix[:-1, :-1], axis=1).astype(np.float64) iou = np.full(self._num_classes, np.nan) union = pos_gt + pos_pred - tp iou_valid = (pos_gt > 0) & (union > 0) iou[iou_valid] = tp[iou_valid] / (union[iou_valid] + 1e-8) miou = np.nanmean(iou) * 100 pacc = np.sum(tp) / (np.sum(pos_gt) + 1e-8) * 100 logger.info(f'mIoU / pACC: {miou:.3f} / {pacc:.3f}') return miou, pacc ``` -------------------------------- ### Perform Semantic and Instance Inference Source: https://context7.com/nicehuster/sympoint/llms.txt Methods to process raw model outputs into final semantic scores and instance predictions using softmax and sigmoid activations. ```python def semantic_inference(self, mask_cls, mask_pred): mask_cls = F.softmax(mask_cls, dim=-1)[..., :-1] mask_pred = mask_pred.sigmoid() semseg = torch.einsum("bqc,bqg->bgc", mask_cls, mask_pred) return semseg[0] def instance_inference(self, mask_cls, mask_pred, overlap_threshold=0.8): # ... NMS-style filtering logic ... return results ``` -------------------------------- ### Define SVG Categories and Dataset Class Source: https://context7.com/nicehuster/sympoint/llms.txt Defines the semantic categories for floor plan elements and initializes the SVGDataset class. This class handles loading JSON files, normalizing coordinates, and preparing data for training or testing. ```python SVG_CATEGORIES = [{"id": 1, "name": "single door", "isthing": 1}, {"id": 33, "name": "wall", "isthing": 0}] class SVGDataset(Dataset): CLASSES = tuple([x["name"] for x in SVG_CATEGORIES]) def __init__(self, data_root, split, data_norm, aug, repeat=1, logger=None): self.split = split self.data_list = glob(osp.join(data_root, "*.json")) ``` -------------------------------- ### Parse and Convert SVG Floor Plans Source: https://context7.com/nicehuster/sympoint/llms.txt This utility parses SVG files to extract geometric primitives like lines, arcs, and circles. It converts these vector elements into a JSON format containing point coordinates, semantic labels, and instance IDs suitable for point-based deep learning models. ```python import math import xml.etree.ElementTree as ET from svgpathtools import parse_path LABEL_NUM = 35 COMMANDS = ['Line', 'Arc', 'circle', 'ellipse'] def parse_svg(svg_file): tree = ET.parse(svg_file) root = tree.getroot() ns = root.tag[:-3] minx, miny, width, height = [int(float(x)) for x in root.attrib['viewBox'].split(' ')] commands, args, lengths = [], [], [] semanticIds, instanceIds = [], [] for g in root.iter(ns + 'g'): for path in g.iter(ns + 'path'): path_repre = parse_path(path.attrib['d']) path_type = path_repre[0].__class__.__name__ commands.append(COMMANDS.index(path_type)) lengths.append(path_repre.length()) semanticIds.append(int(path.attrib.get('semanticId', LABEL_NUM + 1)) - 1) instanceIds.append(int(path.attrib.get('instanceId', -1))) inds = [0, 1/3, 2/3, 1.0] arg = [] for ind in inds: point = path_repre.point(ind) arg.extend([point.real, point.imag]) args.append(arg) for circle in g.iter(ns + 'circle'): cx, cy = float(circle.attrib['cx']), float(circle.attrib['cy']) r = float(circle.attrib['r']) commands.append(COMMANDS.index("circle")) lengths.append(2 * math.pi * r) semanticIds.append(int(circle.attrib.get('semanticId', LABEL_NUM + 1)) - 1) instanceIds.append(int(circle.attrib.get('instanceId', -1))) thetas = [0, math.pi/2, math.pi, 3*math.pi/2] arg = [] for theta in thetas: arg.extend([cx + r * math.cos(theta), cy + r * math.sin(theta)]) args.append(arg) return {"commands": commands, "args": args, "lengths": lengths, "semanticIds": semanticIds, "instanceIds": instanceIds, "width": width, "height": height} ``` -------------------------------- ### Preprocess SVG to JSON using MMCV Source: https://context7.com/nicehuster/sympoint/llms.txt This script preprocesses SVG files from specified dataset splits (train, val, test) into JSON format. It creates necessary directories, finds all SVG files, and uses MMCV's parallel processing to efficiently convert each SVG to a JSON file, saving them in a designated directory. Dependencies include 'os', 'glob', and 'mmcv'. ```python import os import glob import mmcv for split in ["train", "val", "test"]: data_dir = f"./dataset/{split}/{split}/svg_gt" save_dir = f"./dataset/{split}/jsons" os.makedirs(save_dir, exist_ok=True) svg_paths = glob.glob(os.path.join(data_dir, '*.svg')) inputs = [[svg_path, save_dir] for svg_path in svg_paths] def process(data): svg_file, save_dir = data json_dicts = parse_svg(svg_file) # Assuming parse_svg is defined elsewhere filename = svg_file.split("/")[-1].replace(".svg", ".json") json.dump(json_dicts, open(os.path.join(save_dir, filename), 'w'), indent=4) ``` -------------------------------- ### Define SVGNet Model Architecture Source: https://context7.com/nicehuster/sympoint/llms.txt The SVGNet class serves as the primary model wrapper, integrating a PointTransformer backbone and a mask decoder. It handles the forward pass, target preparation for training, and inference logic for semantic and instance segmentation. ```python class SVGNet(nn.Module): def __init__(self, cfg, criterion=None): super().__init__() self.criterion = criterion self.backbone = PointTransformer(cfg) self.decoder = Decoder(cfg, self.backbone.planes) self.num_classes = cfg.semantic_classes self.test_object_score = 0.1 def forward(self, batch, return_loss=True): coords, feats, semantic_labels, offsets, lengths = batch return self._forward(coords, feats, offsets, semantic_labels, lengths, return_loss) ``` -------------------------------- ### Validation Loop with Evaluation Metrics Source: https://context7.com/nicehuster/sympoint/llms.txt Conducts the validation process for a given epoch. It iterates through the validation data, performs inference, and calculates semantic and instance segmentation evaluation metrics (mIoU, Acc, sPQ, sRQ, sSQ). ```python def validate(epoch, model, val_loader, cfg, logger, writer): sem_eval = PointWiseEval(num_classes=cfg.model.semantic_classes, gpu_num=dist.get_world_size()) inst_eval = InstanceEval(num_classes=cfg.model.semantic_classes, gpu_num=dist.get_world_size()) with torch.no_grad(): model.eval() for batch in val_loader: with torch.cuda.amp.autocast(enabled=cfg.fp16): res, loss, _ = model(batch) sem_preds = torch.argmax(res["semantic_scores"], dim=1).cpu().numpy() sem_gts = res["semantic_labels"].cpu().numpy() sem_eval.update(sem_preds, sem_gts) inst_eval.update(res["instances"], res["targets"], res["lengths"]) miou, acc = sem_eval.get_eval(logger) sPQ, sRQ, sSQ = inst_eval.get_eval(logger) return sPQ ``` -------------------------------- ### Load and Process SVG Data Source: https://context7.com/nicehuster/sympoint/llms.txt The static load method parses JSON files, converts path data into point clouds, and computes geometric features like arc angles and command types. It outputs normalized coordinates, features, and semantic/instance labels. ```python @staticmethod def load(json_file, idx, min_points=2048): data = json.load(open(json_file)) args = np.array(data["args"]).reshape(-1, 8) / 140 coord = np.zeros((max_num, 3)) coord[:num, 0] = np.mean(args[:, 0::2], axis=1) coord[:num, 1] = np.mean(args[:, 1::2], axis=1) return coord, feat, label, lengths ``` -------------------------------- ### PointWiseEval - Semantic Segmentation Evaluation Source: https://context7.com/nicehuster/sympoint/llms.txt Evaluates semantic segmentation performance using metrics like mIoU and pixel accuracy. It handles distributed training by aggregating confusion matrices across GPUs. ```APIDOC ## PointWiseEval Class ### Description Provides methods for calculating semantic segmentation evaluation metrics, specifically Mean Intersection over Union (mIoU) and pixel accuracy (pACC). ### Initialization ```python PointWiseEval(num_classes=35, ignore_label=35, gpu_num=1) ``` - **num_classes** (int) - The total number of classes for segmentation. - **ignore_label** (int) - The label to ignore during evaluation. - **gpu_num** (int) - The number of GPUs used in distributed training. ### Methods #### update(pred_sem, gt_sem) Updates the internal confusion matrix with predictions and ground truth for a batch. - **pred_sem** (np.ndarray or torch.Tensor) - Predicted semantic segmentation map. - **gt_sem** (np.ndarray or torch.Tensor) - Ground truth semantic segmentation map. #### get_eval(logger) Computes and logs mIoU and pACC. If distributed, it first gathers confusion matrices from all GPUs. - **logger** (logging.Logger) - A logger object to output evaluation results. ### Metrics Computed - **mIoU**: Mean Intersection over Union. - **pACC**: Pixel Accuracy. ### Example Usage ```python eval_metric = PointWiseEval(num_classes=35, ignore_label=35, gpu_num=1) # ... training loop ... pred_sem = ... # predicted segmentation gt_sem = ... # ground truth segmentation eval_metric.update(pred_sem, gt_sem) # ... after epoch ... miou, pacc = eval_metric.get_eval(logger) ``` ``` -------------------------------- ### Sigmoid Cross-Entropy Loss for Masks in PyTorch Source: https://context7.com/nicehuster/sympoint/llms.txt Calculates the sigmoid cross-entropy loss for mask predictions. This loss is suitable for binary segmentation tasks where the output is a probability map. It uses PyTorch's binary_cross_entropy_with_logits for efficient computation. Dependencies include PyTorch. ```python import torch import torch.nn.functional as F from torch import nn def sigmoid_ce_loss(inputs: torch.Tensor, targets: torch.Tensor, num_masks: float): """Compute sigmoid cross-entropy loss for masks.""" loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction="none") return loss.mean(1).sum() / num_masks ``` -------------------------------- ### Collate Variable-Size Point Clouds Source: https://context7.com/nicehuster/sympoint/llms.txt A custom collate function designed to handle batches of variable-length point clouds by concatenating tensors and calculating offsets for the model backbone. ```python def collate_fn(self, batch): coord, feat, label, lengths = list(zip(*batch)) offset, count = [], 0 for item in coord: count += item.shape[0] offset.append(count) return torch.cat(coord), torch.cat(feat), torch.cat(label), torch.IntTensor(offset), lengths ``` -------------------------------- ### SetCriterion for Panoptic Segmentation Loss in PyTorch Source: https://context7.com/nicehuster/sympoint/llms.txt Implements the criterion for computing the total loss in panoptic segmentation. It combines label classification loss (cross-entropy) and mask losses (sigmoid CE and Dice loss). The class handles matching predictions to targets and aggregating losses, including auxiliary losses from intermediate layers. Dependencies include PyTorch and a matcher object. ```python import torch import torch.nn.functional as F from torch import nn class SetCriterion(nn.Module): """Loss computation for mask-based panoptic segmentation.""" def __init__(self, matcher, weight_dict, cfg): super().__init__() self.num_classes = cfg.num_classes self.matcher = matcher self.weight_dict = weight_dict self.eos_coef = cfg.eos_coef self.losses = cfg.losses # ['labels', 'masks'] # Class weights with lower weight for no-object class empty_weight = torch.ones(self.num_classes + 1) empty_weight[-1] = self.eos_coef self.register_buffer("empty_weight", empty_weight) def loss_labels(self, outputs, targets, indices, num_masks): """Classification loss (cross-entropy).""" src_logits = outputs["pred_logits"].float() idx = self._get_src_permutation_idx(indices) target_classes_o = torch.cat([t["labels"][J] for t, (_, J) in zip(targets, indices)]) target_classes = torch.full(src_logits.shape[:2], self.num_classes, dtype=torch.int64, device=src_logits.device) target_classes[idx] = target_classes_o loss_ce = F.cross_entropy(src_logits.transpose(1, 2), target_classes, self.empty_weight) return {"loss_ce": loss_ce * self.weight_dict["loss_ce"]} def loss_masks(self, outputs, targets, indices, num_masks): """Mask losses: sigmoid CE and DICE.""" src_idx = self._get_src_permutation_idx(indices) tgt_idx = self._get_tgt_permutation_idx(indices) src_masks = outputs["pred_masks"][src_idx] target_masks = torch.cat([t["masks"].transpose(0, 1).unsqueeze(0) for t in targets], dim=0) target_masks = target_masks[tgt_idx] return { "loss_mask": sigmoid_ce_loss(src_masks, target_masks, num_masks) * self.weight_dict["loss_mask"], "loss_dice": dice_loss(src_masks, target_masks, num_masks) * self.weight_dict["loss_dice"], } def forward(self, outputs, targets): """Compute total loss with auxiliary losses.""" indices = self.matcher(outputs, targets) num_masks = sum(len(t["labels"]) for t in targets) num_masks = max(num_masks, 1) losses = {} for loss in self.losses: losses.update(self.get_loss(loss, outputs, targets, indices, num_masks)) # Auxiliary losses from intermediate decoder layers if "aux_outputs" in outputs: for i, aux_outputs in enumerate(outputs["aux_outputs"]): indices = self.matcher(aux_outputs, targets) for loss in self.losses: l_dict = self.get_loss(loss, aux_outputs, targets, indices, num_masks) l_dict = {k + f"_{i}": v for k, v in l_dict.items()} losses.update(l_dict) return losses ``` -------------------------------- ### SetCriterion Loss Functions Source: https://context7.com/nicehuster/sympoint/llms.txt Computes classification, mask, and dice losses for training the panoptic model. These functions are essential for evaluating the model's performance during training by quantifying the difference between predictions and ground truth. ```python def set_criterion_loss(outputs, targets): # Placeholder for actual loss calculation # This would involve using the indices from the HungarianMatcher # and computing classification, mask, and dice losses. pass ``` -------------------------------- ### Dice Loss Calculation in PyTorch Source: https://context7.com/nicehuster/sympoint/llms.txt Computes the Dice loss for mask predictions, a common metric for segmentation tasks. It takes model inputs and target masks, applies sigmoid to inputs, and calculates the loss based on the overlap between predicted and true masks. Dependencies include PyTorch. ```python import torch import torch.nn.functional as F from torch import nn def dice_loss(inputs: torch.Tensor, targets: torch.Tensor, num_masks: float): """Compute DICE loss for mask predictions.""" inputs = inputs.sigmoid().flatten(1) numerator = 2 * (inputs * targets).sum(-1) denominator = inputs.sum(-1) + targets.sum(-1) loss = 1 - (numerator + 1) / (denominator + 1) return loss.sum() / num_masks ``` -------------------------------- ### SymPoint Paper Citation Source: https://github.com/nicehuster/sympoint/blob/main/README.md This is a BibTeX entry for citing the SymPoint paper. It includes author information, title, journal, and publication year, useful for academic references. ```bibtex @article{liu2024symbol, title={Symbol as Points: Panoptic Symbol Spotting via Point-based Representation}, author={Liu, Wenlong and Yang, Tianyu and Wang, Yuhan and Yu, Qizhi and Zhang, Lei}, journal={arXiv preprint arXiv:2401.10556}, year={2024} } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.