{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":84969,"databundleVersionId":10033515},{"sourceType":"datasetVersion","sourceId":10515368,"datasetId":6508773,"databundleVersionId":10845266},{"sourceType":"datasetVersion","sourceId":9867543,"datasetId":6040935,"databundleVersionId":10120220},{"sourceType":"datasetVersion","sourceId":10543825,"datasetId":6523855,"databundleVersionId":10875738},{"sourceType":"datasetVersion","sourceId":10567110,"datasetId":6538860,"databundleVersionId":10900984},{"sourceType":"datasetVersion","sourceId":10581910,"datasetId":6548623,"databundleVersionId":10917282},{"sourceType":"datasetVersion","sourceId":10606606,"datasetId":6565071,"databundleVersionId":10944527},{"sourceType":"datasetVersion","sourceId":10553340,"datasetId":6529589,"databundleVersionId":10886136},{"sourceType":"datasetVersion","sourceId":10649620,"datasetId":6594262,"databundleVersionId":10992024},{"sourceType":"modelInstanceVersion","sourceId":6108,"databundleVersionId":7429378,"modelInstanceId":4651},{"sourceType":"modelInstanceVersion","sourceId":6106,"databundleVersionId":7429374,"modelInstanceId":4649},{"sourceType":"modelInstanceVersion","sourceId":6107,"databundleVersionId":7429375,"modelInstanceId":4650},{"sourceType":"kernelVersion","sourceId":220259087}],"dockerImageVersionId":30805,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":234.328586,"end_time":"2024-12-04T21:48:03.797664","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2024-12-04T21:44:09.469078","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Install and Import modules","metadata":{}},{"cell_type":"markdown","source":"!tar xfvz /kaggle/input/ultralytics-for-offline-install/archive.tar.gz\n!pip install --no-index --find-links=./packages ultralytics\n!rm -rf ./packages","metadata":{"execution":{"iopub.status.busy":"2025-01-19T12:30:17.703868Z","iopub.execute_input":"2025-01-19T12:30:17.704153Z","iopub.status.idle":"2025-01-19T12:31:14.025459Z","shell.execute_reply.started":"2025-01-19T12:30:17.704121Z","shell.execute_reply":"2025-01-19T12:31:14.024443Z"}}},{"cell_type":"code","source":"!cp -r '/kaggle/input/hengck-czii-cryo-et-01/wheel_file' '/kaggle/working/'\n!pip install /kaggle/working/wheel_file/asciitree-0.3.3/asciitree-0.3.3\n!pip install --no-index --find-links=/kaggle/working/wheel_file zarr","metadata":{"papermill":{"duration":52.635478,"end_time":"2024-12-04T21:46:09.434099","exception":false,"start_time":"2024-12-04T21:45:16.798621","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:35:14.289007Z","iopub.execute_input":"2025-02-03T11:35:14.289334Z","iopub.status.idle":"2025-02-03T11:36:07.637845Z","shell.execute_reply.started":"2025-02-03T11:35:14.289293Z","shell.execute_reply":"2025-02-03T11:36:07.636982Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport cv2\nfrom tqdm.notebook import tqdm\n\nimport tensorflow as tf\nfrom tensorflow.keras import *\nfrom tensorflow.keras.optimizers import AdamW\nfrom tensorflow.keras.callbacks import *\nimport keras_cv\nimport zarr\nfrom tqdm import tqdm\nimport glob, os\nimport cv2\nimport math\n\nBATCH_SIZE = 1\nAUTO = tf.data.AUTOTUNE\n\nfrom scipy.spatial import cKDTree\nfrom collections import defaultdict","metadata":{"papermill":{"duration":6.112534,"end_time":"2024-12-04T21:46:15.553505","exception":false,"start_time":"2024-12-04T21:46:09.440971","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:36:07.640630Z","iopub.execute_input":"2025-02-03T11:36:07.641462Z","iopub.status.idle":"2025-02-03T11:36:23.449400Z","shell.execute_reply.started":"2025-02-03T11:36:07.641397Z","shell.execute_reply":"2025-02-03T11:36:23.448739Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We use a recursive function in this notebook, and we change the settings to explore the graph deep enough.","metadata":{}},{"cell_type":"code","source":"import sys\nsys.setrecursionlimit(10000)\nimport warnings\nwarnings.simplefilter('ignore')\nnp.warnings = warnings","metadata":{"papermill":{"duration":0.013236,"end_time":"2024-12-04T21:46:16.261298","exception":false,"start_time":"2024-12-04T21:46:16.248062","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:36:23.450400Z","iopub.execute_input":"2025-02-03T11:36:23.451054Z","iopub.status.idle":"2025-02-03T11:36:23.455244Z","shell.execute_reply.started":"2025-02-03T11:36:23.451023Z","shell.execute_reply":"2025-02-03T11:36:23.454439Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"runs_path = '/kaggle/input/czii-cryo-et-object-identification/test/static/ExperimentRuns/*'\nruns = sorted(glob.glob(runs_path))\nruns = [os.path.basename(run) for run in runs]\n#sp = len(runs)//2\n#runs1 = runs[:sp]\n#runs1[:5]\n#add by @minfuka\n#runs2 = runs[sp:]\nruns[:5]","metadata":{"papermill":{"duration":0.02108,"end_time":"2024-12-04T21:46:18.425141","exception":false,"start_time":"2024-12-04T21:46:18.404061","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:36:23.456570Z","iopub.execute_input":"2025-02-03T11:36:23.456800Z","iopub.status.idle":"2025-02-03T11:36:23.479384Z","shell.execute_reply.started":"2025-02-03T11:36:23.456778Z","shell.execute_reply":"2025-02-03T11:36:23.478695Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##### Information about labels","metadata":{"papermill":{"duration":0.006468,"end_time":"2024-12-04T21:46:18.438503","exception":false,"start_time":"2024-12-04T21:46:18.432035","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-01-13T03:59:47.872507Z","iopub.execute_input":"2025-01-13T03:59:47.873117Z","iopub.status.idle":"2025-01-13T03:59:47.878581Z","shell.execute_reply.started":"2025-01-13T03:59:47.873071Z","shell.execute_reply":"2025-01-13T03:59:47.877163Z"}}},{"cell_type":"code","source":"particle_names = ['apo-ferritin', 'beta-amylase', 'beta-galactosidase', 'ribosome', 'thyroglobulin', 'virus-like-particle']","metadata":{"papermill":{"duration":0.012963,"end_time":"2024-12-04T21:46:18.45793","exception":false,"start_time":"2024-12-04T21:46:18.444967","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:36:23.480288Z","iopub.execute_input":"2025-02-03T11:36:23.480564Z","iopub.status.idle":"2025-02-03T11:36:23.484303Z","shell.execute_reply.started":"2025-02-03T11:36:23.480539Z","shell.execute_reply":"2025-02-03T11:36:23.483490Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"p2i_dict = {\n        'apo-ferritin': 0,\n        'beta-amylase': 1,\n        'beta-galactosidase': 2,\n        'ribosome': 3,\n        'thyroglobulin': 4,\n        'virus-like-particle': 5\n    }\n\ni2p = {v:k for k, v in p2i_dict.items()}","metadata":{"papermill":{"duration":0.012981,"end_time":"2024-12-04T21:46:18.477781","exception":false,"start_time":"2024-12-04T21:46:18.4648","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:36:23.485254Z","iopub.execute_input":"2025-02-03T11:36:23.485575Z","iopub.status.idle":"2025-02-03T11:36:23.494156Z","shell.execute_reply.started":"2025-02-03T11:36:23.485550Z","shell.execute_reply":"2025-02-03T11:36:23.493370Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"particle_radius = {\n        'apo-ferritin': 60,\n        'beta-amylase': 65,\n        'beta-galactosidase': 90,\n        'ribosome': 150,\n        'thyroglobulin': 130,\n        'virus-like-particle': 135,\n    }","metadata":{"papermill":{"duration":0.01279,"end_time":"2024-12-04T21:46:18.497067","exception":false,"start_time":"2024-12-04T21:46:18.484277","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:36:23.495992Z","iopub.execute_input":"2025-02-03T11:36:23.496230Z","iopub.status.idle":"2025-02-03T11:36:23.504513Z","shell.execute_reply.started":"2025-02-03T11:36:23.496206Z","shell.execute_reply":"2025-02-03T11:36:23.503594Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Prepare trained YOLO model","metadata":{}},{"cell_type":"code","source":"class_mapping = i2p\nmodel_list = []\nfor index in [1,2,3,4]:\n    #backbone = keras_cv.models.YOLOV8Backbone.from_preset(\"yolo_v8_s_backbone_coco\", include_rescaling = True)\n    backbone = keras_cv.models.YOLOV8Backbone.from_preset(\"yolo_v8_l_backbone_coco\", include_rescaling = True)\n    YOLOV8_model = keras_cv.models.YOLOV8Detector(num_classes=len(class_mapping),bounding_box_format = \"xyxy\", backbone = backbone, fpn_depth = 5)\n    #YOLOV8_model.load_weights(f'/kaggle/input/final-prediction-model/yolo_acne_detection_{index}.weights.h5')\n    #YOLOV8_model.load_weights(f'/kaggle/input/czii-cryoet-kerascv-yolo-v8-final/yolo_acne_detection_{index}.weights.h5')\n    #YOLOV8_model.load_weights(f'/kaggle/input/model-0122/yolo_acne_detection_{index}.weights.h5')\n    #YOLOV8_model.load_weights(f'/kaggle/input/model-0134/yolo_acne_detection_{index}.weights.h5')\n    #YOLOV8_model.load_weights(f'/kaggle/input/model-0124/yolo_acne_detection_{index}.weights.h5')\n    if index != 99:\n        #YOLOV8_model.load_weights(f'/kaggle/input/model-0126/yolo_acne_detection_{index}.weights.h5')\n        YOLOV8_model.load_weights(f'/kaggle/input/model-0203/yolo_acne_detection_{index}.weights.h5')\n    else:\n        YOLOV8_model.load_weights(f'/kaggle/input/czii-cryoet-kerascv-yolo-v8-final/yolo_acne_detection_{index}.weights.h5')\n    model_list.append(YOLOV8_model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:41:32.251585Z","iopub.execute_input":"2025-02-03T11:41:32.252583Z","iopub.status.idle":"2025-02-03T11:41:55.318605Z","shell.execute_reply.started":"2025-02-03T11:41:32.252546Z","shell.execute_reply":"2025-02-03T11:41:55.317880Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# define Main process class\nThere are many variables and functions going back and forth. We can easily manage variables by defining classes.","metadata":{}},{"cell_type":"code","source":"# add by @sesasj\nclass UnionFind:\n    def __init__(self, size):\n        self.parent = np.arange(size)\n        self.rank = np.zeros(size, dtype=int)\n\n    def find(self, u):\n        if self.parent[u] != u:\n            self.parent[u] = self.find(self.parent[u])  \n        return self.parent[u]\n\n    def union(self, u, v):\n        u_root = self.find(u)\n        v_root = self.find(v)\n        if u_root == v_root:\n            return\n            \n        if self.rank[u_root] < self.rank[v_root]:\n            self.parent[u_root] = v_root\n        else:\n            self.parent[v_root] = u_root\n            if self.rank[u_root] == self.rank[v_root]:\n                self.rank[u_root] += 1\nclass PredictionAggregator:\n    def __init__(self, conf_coef=0.75):\n        self.conf_coef = conf_coef\n        self.particle_confs = np.array([0.5, 0.0, 0.2, 0.5, 0.2, 0.4])  #np.array([0.5, 0.0, 0.2, 0.5, 0.2, 0.4])    \n    def convert_to_8bit(self, volume):\n        lower, upper = np.percentile(volume, (0.5, 99.5))\n        clipped = np.clip(volume, lower, upper)\n        scaled = ((clipped - lower) / (upper - lower + 1e-12) * 255).astype(np.uint8)\n        return scaled\n    def load_ds(self,imgabs):\n        #print(imgabs)\n        img = tf.stack([imgabs,imgabs,imgabs], axis=-1)\n        img = tf.image.resize(img, (640,640))\n        img = tf.cast(img, tf.float32) \n        #print(img)\n        return img\n        \n    def make_predictions(self, run_id, model_list):\n        volume_path = f'/kaggle/input/czii-cryo-et-object-identification/test/static/ExperimentRuns/{run_id}/VoxelSpacing10.000/denoised.zarr'\n        volume = zarr.open(volume_path, mode='r')\n        vol = volume[0]\n        vol2 = self.convert_to_8bit(vol)\n        wide = vol2.shape[2]\n        height = vol2.shape[1]\n        n_imgs =  vol2.shape[0]\n        print(vol2.shape)\n        test_loader = tf.data.Dataset.from_tensor_slices(vol2)\n        test_dataset = (test_loader\n                 .map(self.load_ds, num_parallel_calls = AUTO)\n                 .ragged_batch(1, drop_remainder = True)\n                 .prefetch(AUTO))\n        detections = {\n            'particle_type': [],\n            'confidence': [],\n            'x': [],\n            'y': [],\n            'z': []\n        }\n        pts = []\n        confs = []\n        xs = []\n        ys = []\n        zs = []\n        \n        for model in model_list:\n            pre_datas = model.predict(test_dataset,verbose = 1)\n            for i in range(n_imgs):\n                boxes = pre_datas[\"boxes\"].squeeze()[i]\n                cls = pre_datas[\"classes\"].squeeze()[i]\n                confs_ = pre_datas[\"confidence\"].squeeze()[i]\n                for ii in range(len(cls)):\n                    if cls[ii] == -1:\n                        continue\n                    ptype = cls[ii] # particle type\n                    ptype = i2p[ptype]\n                    conf = confs_[ii] # confidence score\n                    # YOLO can infer (start_x, end_x, start_y, end_y)\n                    xc = (boxes[ii,0] + boxes[ii,2]) / 2.0 * 10 * wide/640\n                    yc = (boxes[ii,1] + boxes[ii,3]) / 2.0 * 10 * height/640\n                    zc = i * 10 + 5\n                    pts.append(ptype)\n                    confs.append(conf)\n                    xs.append(xc)\n                    ys.append(yc)\n                    zs.append(zc)  \n        particle_types = np.array(pts)#detections['particle_type'])\n        confidences = np.array(confs)#detections['confidence'])\n        xs = np.array(xs)#detections['x'])\n        ys = np.array(ys)#detections['y'])\n        zs = np.array(zs)#detections['z'])\n\n        aggregated_data = []\n\n        for idx, particle in enumerate(particle_names):\n            if particle == 'beta-amylase':\n                continue \n\n            mask = (particle_types == particle)\n            if not np.any(mask):\n                continue  \n                \n            particle_confidences = confidences[mask]\n            particle_xs = xs[mask]\n            particle_ys = ys[mask]\n            particle_zs = zs[mask]\n            # -------------modified by @sersasj ------------------------\n            coords = np.vstack((particle_xs, particle_ys, particle_zs)).T\n\n           \n            z_distance = 30#30 # How many slices can you \"jump\" to aggregate predictions 10 = 1, 20 = 2...\n            xy_distance = 20#20 # xy_tol_p2 in original code by ITK8191\n            \n            max_distance = math.sqrt(z_distance**2 + xy_distance**2)\n            tree = cKDTree(coords)            \n            pairs = tree.query_pairs(r=max_distance, p=2)\n\n            \n            uf = UnionFind(len(coords))\n            \n            coords_xy = coords[:, :2]\n            coords_z = coords[:, 2]\n            for u, v in pairs:\n                z_diff = abs(coords_z[u] - coords_z[v])\n                if z_diff > z_distance:\n                    continue  \n\n                xy_diff = np.linalg.norm(coords_xy[u] - coords_xy[v])\n                if xy_diff > xy_distance:\n                    continue  \n\n                uf.union(u, v)\n\n            roots = np.array([uf.find(i) for i in range(len(coords))])\n            unique_roots, inverse_indices, counts = np.unique(roots, return_inverse=True, return_counts=True)\n            conf_sums = np.bincount(inverse_indices, weights=particle_confidences)\n            \n            aggregated_confidences = conf_sums / (counts ** self.conf_coef)\n            print(particle)\n            #print(len(aggregated_confidences),\"###\",aggregated_confidences,\"####\",counts)\n            '''\n            apo-ferritin (easy)\n            beta-amylase (impossible, not scored)\n            beta-galactosidase (hard)\n            ribosome (easy)\n            thyroglobulin (hard)\n            virus-like-particle (easy)\n            '''\n            cluster_per_particle = [4,1,2,9,4,8]#[4,1,2,9,4,8] # Update\n            valid_clusters = (counts >= cluster_per_particle[idx]) & (aggregated_confidences > self.particle_confs[idx])\n            #print(len(valid_clusters),\"########\",valid_clusters)\n\n            if not np.any(valid_clusters):\n                continue  \n\n            cluster_ids = unique_roots[valid_clusters]\n\n            centers_x = np.bincount(inverse_indices, weights=particle_xs) / counts\n            centers_y = np.bincount(inverse_indices, weights=particle_ys) / counts\n            centers_z = np.bincount(inverse_indices, weights=particle_zs) / counts\n\n            centers_x = centers_x[valid_clusters]\n            centers_y = centers_y[valid_clusters]\n            centers_z = centers_z[valid_clusters]\n\n            aggregated_df = pd.DataFrame({\n                'experiment': [run_id] * len(centers_x),\n                'particle_type': [particle] * len(centers_x),\n                'x': centers_x,\n                'y': centers_y,\n                'z': centers_z\n            })\n            print(aggregated_df.shape)\n            aggregated_data.append(aggregated_df)\n        if aggregated_data:\n            return pd.concat(aggregated_data, axis=0)\n        else:\n            return pd.DataFrame() ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:41:55.320460Z","iopub.execute_input":"2025-02-03T11:41:55.320743Z","iopub.status.idle":"2025-02-03T11:41:55.341898Z","shell.execute_reply.started":"2025-02-03T11:41:55.320715Z","shell.execute_reply":"2025-02-03T11:41:55.341094Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import time\n# instance main class\naggregator = PredictionAggregator(conf_coef=0.34) #Update0.34\naggregated_results = []\n#add by @minfuka\nfrom concurrent.futures import ProcessPoolExecutor #add by @minfuka\n#add by @minfuka\ndef inference(runs, model_list):\n    subs = []\n    for r in tqdm(runs, total=len(runs)):\n        df = aggregator.make_predictions(r, model_list)\n        subs.append(df)\n    return subs\nstart_time = time.time()\nresults = inference(runs,model_list)\nend_time = time.time()\nestimated_total_time = (end_time - start_time) / len(runs) * 500  \nprint(f'estimated total prediction time for 500 runs: {estimated_total_time:.4f} seconds')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:41:55.491287Z","iopub.execute_input":"2025-02-03T11:41:55.491567Z","iopub.status.idle":"2025-02-03T11:44:11.560071Z","shell.execute_reply.started":"2025-02-03T11:41:55.491540Z","shell.execute_reply":"2025-02-03T11:44:11.559159Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.concat(results)\nsubmission.insert(0, 'id', range(len(submission)))\nsubmission.to_csv(\"submission.csv\", index=False)\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:44:11.561553Z","iopub.execute_input":"2025-02-03T11:44:11.561830Z","iopub.status.idle":"2025-02-03T11:44:11.587727Z","shell.execute_reply.started":"2025-02-03T11:44:11.561803Z","shell.execute_reply":"2025-02-03T11:44:11.586939Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for x in submission[\"particle_type\"].unique():\n    print(x)\nsubmission.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-03T11:44:11.588595Z","iopub.execute_input":"2025-02-03T11:44:11.588837Z","iopub.status.idle":"2025-02-03T11:44:11.598043Z","shell.execute_reply.started":"2025-02-03T11:44:11.588811Z","shell.execute_reply":"2025-02-03T11:44:11.597142Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}