{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":8967887,"sourceType":"datasetVersion","datasetId":5390745},{"sourceId":8968261,"sourceType":"datasetVersion","datasetId":5367510},{"sourceId":9336438,"sourceType":"datasetVersion","datasetId":5654287},{"sourceId":10401678,"sourceType":"datasetVersion","datasetId":5342704}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# coincide_separation  - Detectron2 [Training]","metadata":{}},{"cell_type":"markdown","source":"[Sartorius Segmentation - Detectron2 [Inference]](https://www.kaggle.com/ammarnassanalhajali/sartorius-segmentation-detectron2-inference) ","metadata":{}},{"cell_type":"markdown","source":"## Other notebooks in this competition \n- [Sartorius Segmentation - Keras U-Net[Training]](https://www.kaggle.com/ammarnassanalhajali/sartorius-segmentation-keras-u-net-training)\n- [Sartorius Segmentation - Keras U-Net[Inference]](https://www.kaggle.com/ammarnassanalhajali/sartorius-segmentation-keras-u-net-inference/edit)","metadata":{}},{"cell_type":"markdown","source":"## Install Detectron2\n","metadata":{}},{"cell_type":"code","source":"import torch, torchvision\nimport shutil\nprint(torch.__version__, torch.cuda.is_available())","metadata":{"_kg_hide-output":false,"execution":{"iopub.status.busy":"2025-11-18T11:43:25.311046Z","iopub.execute_input":"2025-11-18T11:43:25.311386Z","iopub.status.idle":"2025-11-18T11:43:25.317772Z","shell.execute_reply.started":"2025-11-18T11:43:25.311362Z","shell.execute_reply":"2025-11-18T11:43:25.316893Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%capture\n!python -m pip install 'git+https://github.com/facebookresearch/detectron2.git'","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2025-11-18T11:43:25.319006Z","iopub.execute_input":"2025-11-18T11:43:25.319322Z","iopub.status.idle":"2025-11-18T11:43:33.799577Z","shell.execute_reply.started":"2025-11-18T11:43:25.319294Z","shell.execute_reply":"2025-11-18T11:43:33.798326Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# importing libraries\n","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport pandas as pd \nfrom tqdm import tqdm\nfrom tqdm import tqdm_notebook as tqdm # progress bar\nfrom datetime import datetime\nimport time\nimport matplotlib.pyplot as plt\nfrom pycocotools.coco import COCO\nimport os, json, cv2, random\nimport skimage.io as io\nimport copy\nfrom pathlib import Path\nfrom typing import Optional\n\n\n\nfrom tqdm import tqdm\nimport itertools\n\nimport torch\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\n\nfrom glob import glob\nimport numba\nfrom numba import jit\n\nimport warnings\nwarnings.filterwarnings('ignore') #Ignore \"future\" warnings and Data-Frame-Slicing warnings.\n\n\n# detectron2\nfrom detectron2.structures import BoxMode\nfrom detectron2 import model_zoo\nfrom detectron2.config import get_cfg\nfrom detectron2.data import DatasetCatalog, MetadataCatalog\nfrom detectron2.engine import DefaultPredictor, DefaultTrainer, launch\nfrom detectron2.evaluation import COCOEvaluator\nfrom detectron2.structures import BoxMode\nfrom detectron2.utils.visualizer import ColorMode\nfrom detectron2.utils.logger import setup_logger\nfrom detectron2.utils.visualizer import Visualizer\n\nfrom detectron2.data import DatasetCatalog, MetadataCatalog, build_detection_test_loader, build_detection_train_loader\nfrom detectron2.data import detection_utils as utils\n\n\nfrom detectron2.data import DatasetCatalog, MetadataCatalog, build_detection_test_loader, build_detection_train_loader\nfrom detectron2.data import detection_utils as utils\nimport detectron2.data.transforms as T\nfrom detectron2.evaluation import COCOEvaluator, inference_on_dataset\n\nsetup_logger()","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:43:33.800909Z","iopub.execute_input":"2025-11-18T11:43:33.801277Z","iopub.status.idle":"2025-11-18T11:43:33.814151Z","shell.execute_reply.started":"2025-11-18T11:43:33.801232Z","shell.execute_reply":"2025-11-18T11:43:33.813186Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install \"protobuf<4.0.0\" --force-reinstall","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-18T11:43:33.816338Z","iopub.execute_input":"2025-11-18T11:43:33.816580Z","iopub.status.idle":"2025-11-18T11:43:46.921526Z","shell.execute_reply.started":"2025-11-18T11:43:33.816561Z","shell.execute_reply":"2025-11-18T11:43:46.920674Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import wandb\n\nwandb.login(key=\"152f9fe95a7ab860e0a400288743fa7139e84e5b\")\nwandb.init(project=\"mask_rcnn_coincide_separation\", sync_tensorboard=True)","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:43:46.922643Z","iopub.execute_input":"2025-11-18T11:43:46.922883Z","iopub.status.idle":"2025-11-18T11:43:55.391440Z","shell.execute_reply.started":"2025-11-18T11:43:46.922849Z","shell.execute_reply":"2025-11-18T11:43:55.390814Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Loading Dataset","metadata":{}},{"cell_type":"code","source":"Data_Resister_training=\"coincide_separation_train\";\nData_Resister_valid=\"coincide_separation_valid\";\nfrom detectron2.data.datasets import register_coco_instances\n# dataDir=Path('../input/sartorius-cell-instance-segmentation/')\n# dataDir_train=Path('/kaggle/input/coco-data/images')\n# dataDir_test=Path('/kaggle/input/coco-data-val/images')\ndataDir_train=Path('/kaggle/input/coco-real-polygon/train_real_polygon/annotations/images')\n# dataDir_test=Path('/kaggle/input/coco-real-polygon/val_real_polygon/annotations/images')\nregister_coco_instances(Data_Resister_training,{}, '/kaggle/input/coco-real-polygon/train_real_polygon/annotations/instances_default.json', dataDir_train)\n# register_coco_instances(Data_Resister_valid,{},'/kaggle/input/coco-data-val/instances.json', dataDir_test)\nregister_coco_instances(Data_Resister_valid,{},'/kaggle/input/coco-real-polygon/val_real_polygon/annotations/instances_default.json', dataDir_train)\n\nmetadata = MetadataCatalog.get(Data_Resister_training)\ndataset_train = DatasetCatalog.get(Data_Resister_training)\ndataset_valid = DatasetCatalog.get(Data_Resister_valid)\n","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:43:55.392231Z","iopub.execute_input":"2025-11-18T11:43:55.392465Z","iopub.status.idle":"2025-11-18T11:43:55.441303Z","shell.execute_reply.started":"2025-11-18T11:43:55.392438Z","shell.execute_reply":"2025-11-18T11:43:55.440765Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Visualization\n* It's also very easy to visualize prepared training dataset with detectron2.\n* It provides Visualizer class, we can use it to draw an image with mask and bounding box as following.","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize =(18,11))\nd=dataset_valid[2]\n# d=dataset_train[2]\nimg = cv2.imread(d[\"file_name\"])\nprint(img.shape)\nv = Visualizer(img[:, :, ::-1],\n                metadata=metadata, \n                scale=1,\n                instance_mode=ColorMode.IMAGE_BW   # remove the colors of unsegmented pixels. This option is only available for segmentation models\n    )\nout = v.draw_dataset_dict(d)\nax.grid(False)\nax.axis('off')\nax.imshow(out.get_image()[:, :, ::-1])","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:43:55.442125Z","iopub.execute_input":"2025-11-18T11:43:55.442375Z","iopub.status.idle":"2025-11-18T11:43:56.046820Z","shell.execute_reply.started":"2025-11-18T11:43:55.442354Z","shell.execute_reply":"2025-11-18T11:43:56.045900Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Augmentation\nThe dataset is transformed by changing the brighness and flipping the image with 50% probability...etc","metadata":{}},{"cell_type":"code","source":"def custom_mapper(dataset_dict):\n    dataset_dict = copy.deepcopy(dataset_dict)\n    image = utils.read_image(dataset_dict[\"file_name\"], format=\"BGR\")\n    transform_list = [\n            T.RandomBrightness(0.9, 1.1),\n            T.RandomContrast(0.9, 1.1),\n            T.RandomSaturation(0.9, 1.1),\n            T.RandomLighting(0.9),\n            T.RandomFlip(prob=0.5, horizontal=False, vertical=True),\n            T.RandomFlip(prob=0.5, horizontal=True, vertical=False),\n    ]\n    image, transforms = T.apply_transform_gens(transform_list, image)\n    dataset_dict[\"image\"] = torch.as_tensor(image.transpose(2, 0, 1).astype(\"float32\"))\n\n    annos = [\n        utils.transform_instance_annotations(obj, transforms, image.shape[:2])\n        for obj in dataset_dict.pop(\"annotations\")\n        if obj.get(\"iscrowd\", 0) == 0\n    ]\n    instances = utils.annotations_to_instances(annos, image.shape[:2])\n    dataset_dict[\"instances\"] = utils.filter_empty_instances(instances)\n    return dataset_dict\nclass AugTrainer(DefaultTrainer):\n    @classmethod\n    def build_train_loader(cls, cfg):\n        return build_detection_train_loader(cfg, mapper=custom_mapper)\n    def build_evaluator(cls, cfg, dataset_name, output_folder=None):\n        return MAPIOUEvaluator(dataset_name)","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:43:56.047780Z","iopub.execute_input":"2025-11-18T11:43:56.048047Z","iopub.status.idle":"2025-11-18T11:43:56.055333Z","shell.execute_reply.started":"2025-11-18T11:43:56.048029Z","shell.execute_reply":"2025-11-18T11:43:56.054631Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Evaluator","metadata":{}},{"cell_type":"code","source":"# # Taken from https://www.kaggle.com/theoviel/competition-metric-map-iou\n# from detectron2.evaluation.evaluator import DatasetEvaluator\n# import pycocotools.mask as mask_util\n# def precision_at(threshold, iou):\n#     matches = iou > threshold\n#     true_positives = np.sum(matches, axis=1) == 1  # Correct objects\n#     false_positives = np.sum(matches, axis=0) == 0  # Missed objects\n#     false_negatives = np.sum(matches, axis=1) == 0  # Extra objects\n#     return np.sum(true_positives), np.sum(false_positives), np.sum(false_negatives)\n\n# # def score(pred, targ):\n# #     pred_masks = pred['instances'].pred_masks.cpu().numpy()\n# #     enc_preds = [mask_util.encode(np.asarray(p, order='F')) for p in pred_masks]\n# #     enc_targs = list(map(lambda x:x['segmentation'], targ))\n# #     ious = mask_util.iou(enc_preds, enc_targs, [0]*len(enc_targs))\n# #     prec = []\n# #     for t in np.arange(0.5, 1.0, 0.05):\n# #         tp, fp, fn = precision_at(t, ious)\n# #         p = tp / (tp + fp + fn)\n# #         prec.append(p)\n# #     return np.mean(prec)\n# def score(pred, targ):\n#     pred_masks = pred['instances'].pred_masks.cpu().numpy()\n#     enc_preds = [mask_util.encode(np.asarray(p, order='F')) for p in pred_masks]\n\n#     # 将 polygon 转换为 RLE 格式\n#     enc_targs = [mask_util.frPyObjects(t['segmentation'], 1024, 768) for t in targ]\n\n#     ious = mask_util.iou(enc_preds, enc_targs, [0]*len(enc_targs))\n    \n#     prec = []\n#     for t in np.arange(0.5, 1.0, 0.05):\n#         tp, fp, fn = precision_at(t, ious)\n#         p = tp / (tp + fp + fn)\n#         prec.append(p)\n#     return np.mean(prec)\n\n# class MAPIOUEvaluator(DatasetEvaluator):\n#     def __init__(self, dataset_name):\n#         dataset_dicts = DatasetCatalog.get(dataset_name)\n#         self.annotations_cache = {item['image_id']:item['annotations'] for item in dataset_dicts}\n            \n#     def reset(self):\n#         self.scores = []\n\n#     def process(self, inputs, outputs):\n#         for inp, out in zip(inputs, outputs):\n#             if len(out['instances']) == 0:\n#                 self.scores.append(0)    \n#             else:\n#                 targ = self.annotations_cache[inp['image_id']]\n#                 self.scores.append(score(out, targ))\n\n#     def evaluate(self):\n#         return {\"MaP IoU\": np.mean(self.scores)}\n\n# class Trainer(DefaultTrainer):\n#     @classmethod\n#     def build_evaluator(cls, cfg, dataset_name, output_folder=None):\n#         return MAPIOUEvaluator(dataset_name)\n    \n","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:43:56.056210Z","iopub.execute_input":"2025-11-18T11:43:56.056521Z","iopub.status.idle":"2025-11-18T11:43:56.072911Z","shell.execute_reply.started":"2025-11-18T11:43:56.056503Z","shell.execute_reply":"2025-11-18T11:43:56.072181Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"cfg = get_cfg()\nconfig_name = \"COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml\" \ncfg.merge_from_file(model_zoo.get_config_file(config_name))\ncfg.DATASETS.TRAIN = (Data_Resister_training,)\ncfg.DATASETS.TEST = (Data_Resister_valid,)\n\ncfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url(\"COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml\")  # 使用预训练权重\n\ncfg.DATALOADER.NUM_WORKERS = 2\ncfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE = 64  # 64 is slower but more accurate (128 faster but less accurate)\ncfg.SOLVER.IMS_PER_BATCH = 2 #(2 is per defaults)\ncfg.MODEL.ROI_HEADS.NUM_CLASSES = 2 # coincide and separation\ncfg.MODEL.MASK_ON = True  # Ensure mask branch is on\n\ncfg.INPUT.MASK_FORMAT='polygon' #当指定bitmask的时候,只有rle格式可以使用，指定polygon的时候只有polygon格式可以使用\ncfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5\n\ncfg.SOLVER.BASE_LR = 0.0005 #(quite high base learning rate but should drop)\ncfg.SOLVER.MOMENTUM = 0.9\ncfg.SOLVER.WEIGHT_DECAY = 0.0005\ncfg.SOLVER.GAMMA = 0.1\n\ncfg.SOLVER.WARMUP_ITERS = 10 #How many iterations to go from 0 to reach base LR\ncfg.SOLVER.MAX_ITER = 20000 #Maximum of iterations 1\ncfg.SOLVER.STEPS = (500, 1000) #At which point to change the LR 0.25,0.5\ncfg.TEST.EVAL_PERIOD = 2000 # 原来是250\ncfg.SOLVER.CHECKPOINT_PERIOD=2000 # 原来是250\n\n\ntrain_name = cfg.DATASETS.TRAIN[0]\nnum_images = len(DatasetCatalog.get(train_name))\n\nepochs = cfg.SOLVER.MAX_ITER * cfg.SOLVER.IMS_PER_BATCH / num_images\nprint(\"训练图片数:\", num_images)\nprint(\"等价 epochs:\", epochs)\n\nos.makedirs(cfg.OUTPUT_DIR, exist_ok=True)\n# 将配置转换并记录到wandb\n# print(cfg.dump())\nconfig_str=cfg.dump()\nconfig_dict = {}\ncurrent_section = None\nfor line in config_str.strip().split('\\n'):\n    if ':' in line:\n        key, val = line.split(':', 1)\n        key, val = key.strip(), val.strip()\n        if val == '':\n            current_section = key  # 开始新的部分\n            config_dict[current_section] = {}\n        elif current_section:\n            config_dict[current_section][key] = val\n        else:\n            config_dict[key] = val  # 对于顶级键值对\n# config_dict = json.dumps(config_dict, indent=4)\nwandb.config.update(config_dict)\n\n# trainer = AugTrainer(cfg) # with  data augmentation  这个需要Polygon标签格式，如果使用rle，报错：’ValueError: Failed to use mask_format=='polygon' from the given annotations!‘\n# trainer = Trainer(cfg)  # without data augmentation\ntrainer = DefaultTrainer(cfg)\ntrainer.resume_or_load(resume=False)\ntrainer.train()","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2025-11-18T11:43:56.075214Z","iopub.execute_input":"2025-11-18T11:43:56.075425Z","iopub.status.idle":"2025-11-18T11:52:14.769172Z","shell.execute_reply.started":"2025-11-18T11:43:56.075408Z","shell.execute_reply":"2025-11-18T11:52:14.767930Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Evaluator\n* Famouns dataset's evaluator is already implemented in detectron2.\n* For example, many kinds of AP (Average Precision) are calculted in COCOEvaluator.\n* **COCOEvaluator calculates AP with IoU from 0.50 to 0.95**","metadata":{}},{"cell_type":"code","source":"evaluator = COCOEvaluator(Data_Resister_valid, cfg, False, output_dir=\"./output/\")\n# cfg.MODEL.WEIGHTS=\"../input/detectron2cell/output/model_final.pth\"\ncfg.MODEL.WEIGHTS=\"/kaggle/working/output/model_final.pth\"\n#cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.2   # set a custom testing threshold\n#cfg.INPUT.MASK_FORMAT='polygon'  # 注意，这里可以设置为polygon\nval_loader = build_detection_test_loader(cfg, Data_Resister_valid)\ninference_on_dataset(trainer.model, val_loader, evaluator)","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:52:14.771446Z","iopub.execute_input":"2025-11-18T11:52:14.771870Z","iopub.status.idle":"2025-11-18T11:52:38.197610Z","shell.execute_reply.started":"2025-11-18T11:52:14.771808Z","shell.execute_reply":"2025-11-18T11:52:38.196972Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 训练集验证一下","metadata":{}},{"cell_type":"code","source":"evaluator = COCOEvaluator(Data_Resister_training, cfg, False, output_dir=\"./output/\")\n# cfg.MODEL.WEIGHTS=\"../input/detectron2cell/output/model_final.pth\"\ncfg.MODEL.WEIGHTS=\"/kaggle/working/output/model_final.pth\"\n#cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.2   # set a custom testing threshold\n#cfg.INPUT.MASK_FORMAT='polygon'  # 注意，这里可以设置为polygon\nval_loader = build_detection_test_loader(cfg, Data_Resister_training)\ninference_on_dataset(trainer.model, val_loader, evaluator)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-18T12:06:46.286334Z","iopub.execute_input":"2025-11-18T12:06:46.286974Z","iopub.status.idle":"2025-11-18T12:06:54.298872Z","shell.execute_reply.started":"2025-11-18T12:06:46.286948Z","shell.execute_reply":"2025-11-18T12:06:54.298262Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nmetrics_df = pd.read_json(\"./output/metrics.json\", orient=\"records\", lines=True)\nmdf = metrics_df.sort_values(\"iteration\")","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:52:38.198719Z","iopub.execute_input":"2025-11-18T11:52:38.198957Z","iopub.status.idle":"2025-11-18T11:52:38.254189Z","shell.execute_reply.started":"2025-11-18T11:52:38.198934Z","shell.execute_reply":"2025-11-18T11:52:38.253561Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Loss curve","metadata":{}},{"cell_type":"code","source":"# 1. Loss curve\nfig, ax = plt.subplots()\n\nmdf1 = mdf[~mdf[\"total_loss\"].isna()]\nax.plot(mdf1[\"iteration\"], mdf1[\"total_loss\"], c=\"C0\", label=\"train\")\nif \"validation_loss\" in mdf.columns:\n    mdf2 = mdf[~mdf[\"validation_loss\"].isna()]\n    ax.plot(mdf2[\"iteration\"], mdf2[\"validation_loss\"], c=\"C1\", label=\"validation\")\n\n# ax.set_ylim([0, 0.5])\nax.legend()\nax.set_title(\"Loss curve\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:52:38.254892Z","iopub.execute_input":"2025-11-18T11:52:38.255081Z","iopub.status.idle":"2025-11-18T11:52:38.444385Z","shell.execute_reply.started":"2025-11-18T11:52:38.255053Z","shell.execute_reply":"2025-11-18T11:52:38.443742Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Accuracy curve","metadata":{}},{"cell_type":"code","source":"# 1. Accuracy curve\nfig, ax = plt.subplots()\n\nmdf1 = mdf[~mdf[\"fast_rcnn/cls_accuracy\"].isna()]\nax.plot(mdf1[\"iteration\"], mdf1[\"fast_rcnn/cls_accuracy\"], c=\"C0\", label=\"train\")\n# ax.set_ylim([0, 0.5])\nax.legend()\nax.set_title(\"Accuracy curve\")\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-11-18T11:52:38.445317Z","iopub.execute_input":"2025-11-18T11:52:38.445763Z","iopub.status.idle":"2025-11-18T11:52:38.616194Z","shell.execute_reply.started":"2025-11-18T11:52:38.445736Z","shell.execute_reply":"2025-11-18T11:52:38.615521Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Predictor","metadata":{}},{"cell_type":"code","source":"cfg.MODEL.WEIGHTS = os.path.join(cfg.OUTPUT_DIR, \"model_final.pth\")\n#cfg.MODEL.WEIGHTS = \"./output/model_final.pth\"\ncfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5   # set a custom testing threshold for this model\ncfg.DATASETS.TEST = (Data_Resister_valid, )\npredictor = DefaultPredictor(cfg)","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:52:38.617013Z","iopub.execute_input":"2025-11-18T11:52:38.617396Z","iopub.status.idle":"2025-11-18T11:52:39.668860Z","shell.execute_reply.started":"2025-11-18T11:52:38.617374Z","shell.execute_reply":"2025-11-18T11:52:39.668283Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## save video (success)","metadata":{}},{"cell_type":"code","source":"import cv2\nimport os\nfrom detectron2.data import MetadataCatalog\nfrom detectron2.utils.visualizer import ColorMode, Visualizer\nfrom detectron2.utils.colormap import colormap\n\nfile_path = '/kaggle/input/particle-video/output_video.mp4'\noutput_path = 'output_video.avi'\n\nif os.path.exists(output_path):\n    os.remove(output_path)\n\nvideo = cv2.VideoCapture(file_path)\nwidth = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))\nheight = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))\nfps = video.get(cv2.CAP_PROP_FPS)\n\nfourcc = cv2.VideoWriter_fourcc(*'XVID')\nout = cv2.VideoWriter(output_path, fourcc, fps, (width, height))\n\n# 获取元数据\nparticle_metadata = MetadataCatalog.get(cfg.DATASETS.TRAIN[0])\n\n# ✅ 关键：设置 thing_colors（RGB格式，0-255）\nparticle_metadata.thing_colors = [\n    (0, 255, 0),    # 类别 0: 纯绿\n    (255, 0, 0)     # 类别 1: 纯红\n]\n\n# ✅ 关键：设置 thing_classes 不能修改\nparticle_metadata.thing_classes = [\"coincide\", \"separation\"]\n\nprint(f\"开始处理... 双类别颜色已锁定。\")\nprint(f\"颜色配置: {particle_metadata.thing_colors}\")\n\nframe_count = 0\nwhile True:\n    ret, frame = video.read()\n    if not ret:\n        break\n    \n    frame_count += 1\n    outputs = predictor(frame)\n    \n    # 1. 获取实例并转到 CPU\n    instances = outputs[\"instances\"].to(\"cpu\")\n\n    # ✅ 关键修改：如果有掩码数据，直接移除它\n    if instances.has(\"pred_masks\"):\n        instances.remove(\"pred_masks\")\n\n    # 2. 初始化可视化工具\n    # 注意：既然不画掩码了，instance_mode 可以改为 IMAGE 或者不传（默认）\n    v = Visualizer(frame[:, :, ::-1], \n                   metadata=particle_metadata, \n                   scale=1.0, \n                   instance_mode=ColorMode.IMAGE) # 这里改回普通模式即可\n    \n    # 3. 绘制（此时 instances 里只有框和类别，没有掩码了）\n    v = v.draw_instance_predictions(instances)\n    \n    result_frame = v.get_image()[:, :, ::-1]\n    \n    out.write(result_frame)\n    \n    if frame_count % 30 == 0:\n        print(f\"已处理 {frame_count} 帧...\")\n\nvideo.release()\nout.release()\nprint(f\"处理完成！总共 {frame_count} 帧\")\n","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:52:39.669788Z","iopub.execute_input":"2025-11-18T11:52:39.670007Z","iopub.status.idle":"2025-11-18T11:54:30.774190Z","shell.execute_reply.started":"2025-11-18T11:52:39.669990Z","shell.execute_reply":"2025-11-18T11:54:30.773396Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from IPython.display import Image, Video\nimport os\ncompressed_output_path = 'maskrcnn-particle.mp4'\n\nos.system(f'ffmpeg -i {output_path} -vcodec libx264 -crf 28 {compressed_output_path}')\n\ndisplay(Video(compressed_output_path, embed=True))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## save pictures","metadata":{}},{"cell_type":"code","source":"# 设定文件路径\nfile_path = '/kaggle/input/particle-video/output_video.mp4'\noutput_dir = './frames'  # 设置保存帧的目录\n\n# 确保输出目录存在\nif os.path.exists(output_dir):\n    shutil.rmtree(output_dir)  # 删除已存在的目录及其所有内容\nos.makedirs(output_dir)  # 创建新的空目录\n\n# 读取视频\nvideo = cv2.VideoCapture(file_path)\nif not video.isOpened():\n    print(\"错误：无法打开视频文件。\")\n    exit()\n\nfile_size = os.path.getsize(file_path)\nprint(\"file_size:\", file_size)\n\nframe_idx = 0  # 帧编号\nwhile True:\n    ret, frame = video.read()\n    if not ret:\n        break\n    \n    # 模型推理\n    outputs = predictor(frame)\n    v = Visualizer(frame[:, :, ::-1], MetadataCatalog.get(cfg.DATASETS.TRAIN[0]), scale=1.2)\n    v = v.draw_instance_predictions(outputs[\"instances\"].to(\"cpu\"))\n    result_frame = v.get_image()[:, :, ::-1]\n    \n    # 保存处理后的视频帧为图片\n    frame_file = os.path.join(output_dir, f\"frame_{frame_idx:04d}.jpg\")\n    cv2.imwrite(frame_file, result_frame)\n    frame_idx += 1\n\nvideo.release()\nprint(\"视频帧已保存到\", output_dir)","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:54:30.775060Z","iopub.execute_input":"2025-11-18T11:54:30.775312Z","iopub.status.idle":"2025-11-18T11:54:31.047031Z","shell.execute_reply.started":"2025-11-18T11:54:30.775293Z","shell.execute_reply":"2025-11-18T11:54:31.045986Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import zipfile\n\n# 打包所有文件到ZIP\nzip_path = './frames.zip'  # ZIP文件的存储路径\nif os.path.exists(zip_path):\n    os.remove(zip_path)  \nwith zipfile.ZipFile(zip_path, 'w') as zipf:\n    for root, dirs, files in os.walk(output_dir):\n        for file in files:\n            zipf.write(os.path.join(root, file), arcname=file)\n\nprint(\"ZIP文件已创建：\", zip_path)","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:54:31.047547Z","iopub.status.idle":"2025-11-18T11:54:31.047787Z","shell.execute_reply.started":"2025-11-18T11:54:31.047672Z","shell.execute_reply":"2025-11-18T11:54:31.047684Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# fig, ax = plt.subplots(4, 1, figsize =(20,50))\n# indices=[ax[0],ax[1],ax[2],ax[3] ]\n# i=-1\n# for d in random.sample(dataset_valid, 4):\n#     i=i+1    \n#     im = cv2.imread(d[\"file_name\"])\n#     outputs = predictor(im)\n#     v = Visualizer(im[:, :, ::-1],\n#                    metadata=metadata, \n#                    scale=1, \n#                    instance_mode=ColorMode.IMAGE_BW   # remove the colors of unsegmented pixels. This option is only available for segmentation models\n#     )\n#     out = v.draw_instance_predictions(outputs[\"instances\"].to(\"cpu\"))\n#     processed_image = out.get_image()[:, :, ::-1]\n#     indices[i].grid(False)\n#     indices[i].imshow(processed_image)\n#     # Log image to wandb\n#     wandb.log({f\"Processed Image {i}\": [wandb.Image(processed_image, caption=f\"Processed Image {i}\")]})\n# wandb.finish()\n","metadata":{"execution":{"iopub.status.busy":"2025-11-18T11:54:31.048556Z","iopub.status.idle":"2025-11-18T11:54:31.048786Z","shell.execute_reply.started":"2025-11-18T11:54:31.048678Z","shell.execute_reply":"2025-11-18T11:54:31.048689Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 同时展示原始图像和预测的图像","metadata":{}},{"cell_type":"code","source":"import cv2\nimport matplotlib.pyplot as plt\nimport random\nimport wandb\nfrom detectron2.utils.visualizer import Visualizer, ColorMode\n\n# 假设 dataset_valid, predictor, metadata 已经定义好了\n\n# 1. 创建 4 行 2 列的子图布局\n# 4 行用于展示 4 组数据，2 列分别用于 (原始图, 预测图)\nfig, ax = plt.subplots(4, 2, figsize =(40, 50)) \ni = -1\n\nfor d in random.sample(dataset_valid, 4):\n    i = i + 1\n    \n    # 读取图像 (BGR 格式)\n    im_bgr = cv2.imread(d[\"file_name\"])\n    # 转换为 RGB 格式用于 Matplotlib\n    im_rgb = im_bgr[:, :, ::-1] \n    \n    # 运行预测\n    outputs = predictor(im_bgr)\n    \n    # 初始化 Visualizer\n    v = Visualizer(im_rgb, # Visualizer 期望 RGB 格式\n                   metadata=metadata, \n                   scale=1, \n                   instance_mode=ColorMode.IMAGE_BW\n    )\n    \n    # 绘制预测结果\n    out = v.draw_instance_predictions(outputs[\"instances\"].to(\"cpu\"))\n    # 获取处理后的图像，并转换回 RGB 格式\n    processed_image = out.get_image()[:, :, ::-1]\n\n    # --- 可视化：在第 i 行的第 0 列展示原始图像 ---\n    # ax 是一个二维 NumPy 数组，所以使用 ax[行, 列] 索引\n    ax[i, 0].grid(False)\n    ax[i, 0].imshow(im_rgb)\n    ax[i, 0].set_title(f\"Original Image {i+1}\", fontsize=20)\n    \n    # --- 可视化：在第 i 行的第 1 列展示预测图像 ---\n    ax[i, 1].grid(False)\n    ax[i, 1].imshow(processed_image)\n    ax[i, 1].set_title(f\"Predicted Image {i+1}\", fontsize=20)\n    \n    # Log image to wandb\n    # 如果想在 wandb 中同时记录两张图，可以分别记录或打包记录\n    wandb.log({\n        f\"Original Image {i+1}\": [wandb.Image(im_rgb, caption=f\"Original {i+1}\")],\n        f\"Processed Image {i+1}\": [wandb.Image(processed_image, caption=f\"Predicted {i+1}\")]\n    })\n\n# 由于 figsize 已经设置，可能需要调整布局以防止标题重叠\nplt.tight_layout() \nwandb.finish()","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}