{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":30201,"databundleVersionId":2750748,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":2724590,"sourceType":"datasetVersion","datasetId":1660631},{"sourceId":11861175,"sourceType":"datasetVersion","datasetId":7453202},{"sourceId":11861346,"sourceType":"datasetVersion","datasetId":7453410},{"sourceId":11867616,"sourceType":"datasetVersion","datasetId":7451568},{"sourceId":11867625,"sourceType":"datasetVersion","datasetId":7457695},{"sourceId":11872743,"sourceType":"datasetVersion","datasetId":7451518}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# 複製整個 detectron2 專案到 working 資料夾（這裡可以寫入）\n!cp -r /kaggle/input/detectron2 /kaggle/working/\n\n# 切換目錄\n%cd /kaggle/working/detectron2\n\n# 安裝成 editable 模式\n!pip install -e .","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nfrom detectron2.data import DatasetCatalog, MetadataCatalog\nfrom detectron2.data.datasets import register_coco_instances\n\ndef register_sartorius_dataset(base_path=\"/kaggle/input/annotations\"):\n    train_json = os.path.join(base_path, \"annotations_train_fixed.json\")\n    val_json = os.path.join(base_path, \"annotations_val_fixed.json\")\n    image_root = \"/kaggle/input/sartorius-cell-instance-segmentation\"\n\n    # 註冊 COCO 格式資料集\n    register_coco_instances(\"sartorius_train\", {}, train_json, image_root)\n    register_coco_instances(\"sartorius_val\", {}, val_json, image_root)\n\n    for d in [\"sartorius_train\", \"sartorius_val\"]:\n        meta = MetadataCatalog.get(d)\n        meta.thing_classes = [\"shsy5y\", \"astro\", \"cort\"]\n        meta.evaluator_type = \"coco\"\n\nregister_sartorius_dataset()\nprint(\"Sartorius dataset registered.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from detectron2.modeling.roi_heads import ROI_HEADS_REGISTRY\n\n# 移除已註冊的 WeightedCascadeROIHeads（如果存在）\nif \"WeightedCascadeROIHeads\" in ROI_HEADS_REGISTRY._obj_map:\n    del ROI_HEADS_REGISTRY._obj_map[\"WeightedCascadeROIHeads\"]\n\nfrom detectron2.modeling.roi_heads.cascade_rcnn import CascadeROIHeads\nfrom detectron2.modeling.roi_heads.fast_rcnn import fast_rcnn_inference\nimport torch\nimport torch.nn.functional as F\n\n@ROI_HEADS_REGISTRY.register()\nclass WeightedCascadeROIHeads(CascadeROIHeads):\n    def __init__(self, cfg, input_shape):\n        super().__init__(cfg, input_shape)\n        self.class_weights = torch.tensor([0.35, 0.30, 0.30, 0.05])\n\n    def forward(self, images, features, proposals, targets=None):\n        if self.training:\n            proposals = self.label_and_sample_proposals(proposals, targets)\n\n        losses = {}\n        deltas = proposals\n        image_sizes = images.image_sizes\n\n        for stage, (box_head, box_predictor) in enumerate(zip(self.box_head, self.box_predictor)):\n            box_features = self.box_pooler(\n                [features[f] for f in self.in_features],\n                [x.proposal_boxes for x in deltas]\n            )\n            box_features = box_head(box_features)\n            predictions = box_predictor(box_features)\n\n            if self.training:\n                gt_classes = torch.cat([p.gt_classes for p in deltas], dim=0)\n                class_weights = self.class_weights.to(predictions[0].device)\n                losses.update({\n                    f\"loss_cls_stage{stage}\": F.cross_entropy(\n                        predictions[0], gt_classes, weight=class_weights\n                    ),\n                    f\"loss_box_reg_stage{stage}\": box_predictor.losses(predictions, deltas)[\"loss_box_reg\"],\n                })\n            else:\n                pred_boxes = box_predictor.box2box_transform.apply_deltas(\n                    predictions[1], torch.cat([x.proposal_boxes.tensor for x in deltas], dim=0)\n                )\n                split_sizes = [len(p) for p in deltas]\n                pred_boxes = pred_boxes.split(split_sizes)\n\n                pred_scores = box_predictor.predict_probs(predictions, deltas)\n\n                pred_instances, _ = fast_rcnn_inference(\n                    pred_boxes,\n                    pred_scores,\n                    image_sizes,\n                    score_thresh=box_predictor.test_score_thresh,\n                    nms_thresh=box_predictor.test_nms_thresh,\n                    topk_per_image=box_predictor.test_topk_per_image,\n                )\n\n                if self.mask_on:\n                    pred_instances = self.forward_with_given_boxes(features, pred_instances)\n                return pred_instances, {}\n\n            # 更新 proposals 以給下一階段使用\n            with torch.no_grad():\n                pred_boxes = box_predictor.box2box_transform.apply_deltas(\n                    predictions[1], torch.cat([x.proposal_boxes.tensor for x in deltas], dim=0)\n                )\n                split_sizes = [len(p) for p in deltas]\n                pred_boxes = pred_boxes.split(split_sizes)\n                for i in range(len(deltas)):\n                    deltas[i].proposal_boxes.tensor = pred_boxes[i]\n\n        if self.training and self.mask_on:\n            mask_loss = self._forward_mask(features, deltas)\n            losses.update(mask_loss)\n\n        return deltas, losses","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#!/usr/bin/env python\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n\"\"\"\nDetection Training Script.\n\nThis scripts reads a given config file and runs the training or evaluation.\nIt is an entry point that is made to train standard models in detectron2.\n\nIn order to let one script support training of many models,\nthis script contains logic that are specific to these built-in models and therefore\nmay not be suitable for your own project.\nFor example, your research project perhaps only needs a single \"evaluator\".\n\nTherefore, we recommend you to use detectron2 as an library and take\nthis file as an example of how to use the library.\nYou may want to write your own script with your datasets and other customizations.\n\"\"\"\nfrom detectron2.data import DatasetCatalog\n\n\nimport logging\nimport os\nfrom collections import OrderedDict\nimport torch\n\nimport detectron2.utils.comm as comm\nfrom detectron2.checkpoint import DetectionCheckpointer\nfrom detectron2.config import get_cfg\nfrom detectron2.data import MetadataCatalog\nfrom detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, hooks, launch\nfrom detectron2.evaluation import (\n    CityscapesEvaluator,\n    COCOEvaluator,\n    COCOPanopticEvaluator,\n    DatasetEvaluators,\n    LVISEvaluator,\n    PascalVOCDetectionEvaluator,\n    SemSegEvaluator,\n    verify_results,\n)\nfrom detectron2.modeling import GeneralizedRCNNWithTTA\nfrom detectron2.data import DatasetCatalog\n\n\nclass Trainer(DefaultTrainer):\n    \"\"\"\n    We use the \"DefaultTrainer\" which contains pre-defined default logic for\n    standard training workflow. They may not work for you, especially if you\n    are working on a new research project. In that case you can use the cleaner\n    \"SimpleTrainer\", or write your own training loop. You can use\n    \"tools/plain_train_net.py\" as an example.\n    \"\"\"\n\n    @classmethod\n    def build_evaluator(cls, cfg, dataset_name, output_folder=None):\n        \"\"\"\n        Create evaluator(s) for a given dataset.\n        This uses the special metadata \"evaluator_type\" associated with each builtin dataset.\n        For your own dataset, you can simply create an evaluator manually in your\n        script and do not have to worry about the hacky if-else logic here.\n        \"\"\"\n        if output_folder is None:\n            output_folder = os.path.join(cfg.OUTPUT_DIR, \"inference\")\n        evaluator_list = []\n        evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type\n        if evaluator_type in [\"sem_seg\", \"coco_panoptic_seg\"]:\n            evaluator_list.append(\n                SemSegEvaluator(\n                    dataset_name,\n                    distributed=True,\n                    num_classes=cfg.MODEL.SEM_SEG_HEAD.NUM_CLASSES,\n                    ignore_label=cfg.MODEL.SEM_SEG_HEAD.IGNORE_VALUE,\n                    output_dir=output_folder,\n                )\n            )\n        if evaluator_type in [\"coco\", \"coco_panoptic_seg\"]:\n            evaluator_list.append(COCOEvaluator(dataset_name, cfg, True, output_folder))\n        if evaluator_type == \"coco_panoptic_seg\":\n            evaluator_list.append(COCOPanopticEvaluator(dataset_name, output_folder))\n        elif evaluator_type == \"cityscapes\":\n            assert (\n                torch.cuda.device_count() >= comm.get_rank()\n            ), \"CityscapesEvaluator currently do not work with multiple machines.\"\n            return CityscapesEvaluator(dataset_name)\n        elif evaluator_type == \"pascal_voc\":\n            return PascalVOCDetectionEvaluator(dataset_name)\n        elif evaluator_type == \"lvis\":\n            return LVISEvaluator(dataset_name, cfg, True, output_folder)\n        if len(evaluator_list) == 0:\n            raise NotImplementedError(\n                \"no Evaluator for the dataset {} with the type {}\".format(\n                    dataset_name, evaluator_type\n                )\n            )\n        elif len(evaluator_list) == 1:\n            return evaluator_list[0]\n        return DatasetEvaluators(evaluator_list)\n\n    @classmethod\n    def test_with_TTA(cls, cfg, model):\n        logger = logging.getLogger(\"detectron2.trainer\")\n        # In the end of training, run an evaluation with TTA\n        # Only support some R-CNN models.\n        logger.info(\"Running inference with test-time augmentation ...\")\n        model = GeneralizedRCNNWithTTA(cfg, model)\n        evaluators = [\n            cls.build_evaluator(\n                cfg, name, output_folder=os.path.join(cfg.OUTPUT_DIR, \"inference_TTA\")\n            )\n            for name in cfg.DATASETS.TEST\n        ]\n        res = cls.test(cfg, model, evaluators)\n        res = OrderedDict({k + \"_TTA\": v for k, v in res.items()})\n        return res\n\n\ndef setup(args):\n    \"\"\"\n    Create configs and perform basic setups.\n    \"\"\"\n    cfg = get_cfg()\n    cfg.merge_from_file(args.config_file)\n    cfg.merge_from_list(args.opts)\n    cfg.INPUT.MASK_FORMAT = \"bitmask\"\n    cfg.freeze()\n    default_setup(cfg, args)\n    return cfg\n\n\ndef main(args):\n    cfg = setup(args)\n    \n\n    if args.eval_only:\n        model = Trainer.build_model(cfg)\n        DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load(\n            cfg.MODEL.WEIGHTS, resume=args.resume\n        )\n        res = Trainer.test(cfg, model)\n        if cfg.TEST.AUG.ENABLED:\n            res.update(Trainer.test_with_TTA(cfg, model))\n        if comm.is_main_process():\n            verify_results(cfg, res)\n        return res\n\n    \"\"\"\n    If you'd like to do anything fancier than the standard training logic,\n    consider writing your own training loop or subclassing the trainer.\n    \"\"\"\n    trainer = Trainer(cfg)\n    trainer.resume_or_load(resume=args.resume)\n    if cfg.TEST.AUG.ENABLED:\n        trainer.register_hooks(\n            [hooks.EvalHook(0, lambda: trainer.test_with_TTA(cfg, trainer.model))]\n        )\n    return trainer.train()\n\n\nif __name__ == \"__main__\":\n    args, _ = default_argument_parser().parse_known_args()  # ← 注意：這會回傳 Namespace 和 unknown args list\n\n    # 手動覆蓋你要的參數\n    args.config_file = \"/kaggle/input/myconfig/livecell_config.yaml\"\n    args.num_gpus = 1\n    args.opts = []\n    \n    print(\"Command Line Args:\", args)\n\n    launch(\n        main,\n        args.num_gpus,\n        num_machines=args.num_machines,\n        machine_rank=args.machine_rank,\n        dist_url=args.dist_url,\n        args=(args,),\n    )\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport json\nimport matplotlib.pyplot as plt\nfrom collections import Counter\nimport numpy as np\nfrom pycocotools.coco import COCO\n\n# 修改成你實際的路徑\nANNOTATION_PATH = \"/kaggle/input/annotations/annotations_train_fixed.json\"\nIMAGE_ROOT = \"/kaggle/input/sartorius-cell-instance-segmentation/train\"\n\n# 載入 COCO annotations\ncoco = COCO(ANNOTATION_PATH)\n\nwidths = []\nheights = []\nareas = []\naspect_ratios = []\ncategory_ids = []\n\nfor ann in coco.dataset['annotations']:\n    x, y, w, h = ann['bbox']\n    widths.append(w)\n    heights.append(h)\n    areas.append(w * h)\n    aspect_ratios.append(w / h if h != 0 else 0)\n    category_ids.append(ann['category_id'])\n\n# 分類統計\ncategory_counts = Counter(category_ids)\ncategory_names = {cat['id']: cat['name'] for cat in coco.loadCats(coco.getCatIds())}\ncategory_counts_named = {category_names[k]: v for k, v in category_counts.items()}\n\n# 可視化\nplt.figure(figsize=(15, 4))\n\nplt.subplot(1, 4, 1)\nplt.hist(widths, bins=30)\nplt.title(\"BBox Width\")\n\nplt.subplot(1, 4, 2)\nplt.hist(heights, bins=30)\nplt.title(\"BBox Height\")\n\nplt.subplot(1, 4, 3)\nplt.hist(areas, bins=30)\nplt.title(\"BBox Area\")\n\nplt.subplot(1, 4, 4)\nplt.hist(aspect_ratios, bins=30)\nplt.title(\"Aspect Ratio (w/h)\")\n\nplt.tight_layout()\nplt.show()\n\ncounts = 0\nfreq = []\n# 顯示類別統計\nprint(\"📊 Category Distribution:\")\nfor name, count in category_counts_named.items():\n    print(f\"  - {name}: {count} instances\")\n\n    counts += count\n\nfor name, count in category_counts_named.items():\n\n    freq.append(count / count)\nfreq.append(1.0)\ntotal = sum(freq)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T13:46:55.544379Z","iopub.execute_input":"2025-05-19T13:46:55.54454Z","iopub.status.idle":"2025-05-19T13:46:57.181882Z","shell.execute_reply.started":"2025-05-19T13:46:55.544525Z","shell.execute_reply":"2025-05-19T13:46:57.181093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nfrom pycocotools.coco import COCO\n\n# 替換為你的 train json 路徑\nann_path = \"/kaggle/input/annotations/annotations_train_fixed.json\"\ncoco = COCO(ann_path)\n\nwidths, heights, areas = [], [], []\n\nfor ann in coco.dataset['annotations']:\n    x, y, w, h = ann['bbox']\n    widths.append(w)\n    heights.append(h)\n    areas.append(w * h)\n\n# 計算百分位數\nfor name, values in zip([\"Width\", \"Height\", \"Area\"], [widths, heights, areas]):\n    values = np.array(values)\n    percentiles = np.percentile(values, [0, 10, 25, 50, 75, 90, 100])\n    print(f\"\\n{name} Percentiles:\")\n    for p, val in zip([0, 10, 25, 50, 75, 90, 100], percentiles):\n        print(f\"  {p:>3}% : {val:.2f}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nfrom pycocotools.coco import COCO\nfrom sklearn.cluster import KMeans\nimport matplotlib.pyplot as plt\n\n# 設定你的 annotation 路徑\nann_path = \"/kaggle/input/annotations/annotations_train_fixed.json\"\ncoco = COCO(ann_path)\n\n# 收集所有 bbox 的寬與高\nwidths, heights = [], []\nfor ann in coco.dataset['annotations']:\n    x, y, w, h = ann['bbox']\n    widths.append(w)\n    heights.append(h)\n\nwidths = np.array(widths)\nheights = np.array(heights)\nwh = np.stack([widths, heights], axis=1)\n\n# 使用 KMeans 聚類分析（例如 5 群）\nk = 5\nkmeans = KMeans(n_clusters=k, random_state=42, n_init=10).fit(wh)\nanchors = np.round(kmeans.cluster_centers_, 1)  # (width, height)\n\n# 顯示 anchor 結果\nprint(\"📦 KMeans 建議 anchor 尺寸 (width x height):\")\nfor w, h in anchors:\n    print(f\"- {int(w)} x {int(h)}\")\n\n# 顯示 aspect ratios\nratios = np.round(anchors[:, 0] / anchors[:, 1], 2)\nprint(\"\\n📐 對應 aspect ratios (w/h):\", ratios.tolist())\n\n# 視覺化 anchor 分布\nplt.figure(figsize=(6, 6))\nplt.scatter(widths, heights, alpha=0.2, label='bboxes')\nplt.scatter(anchors[:, 0], anchors[:, 1], color='red', label='anchors')\nplt.xlabel(\"Width\")\nplt.ylabel(\"Height\")\nplt.title(\"KMeans Anchor Clustering\")\nplt.legend()\nplt.grid(True)\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}