{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":91249,"databundleVersionId":11294684,"sourceType":"competition"},{"sourceId":11575583,"sourceType":"datasetVersion","datasetId":7257608},{"sourceId":11599727,"sourceType":"datasetVersion","datasetId":7274698},{"sourceId":11960738,"sourceType":"datasetVersion","datasetId":7250620},{"sourceId":226864880,"sourceType":"kernelVersion"}],"dockerImageVersionId":31011,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from timeit import default_timer as timer\nstart_timer = timer()\n\ntry: \n    import ultralytics\nexcept:\n    pass\n    #!tar cfvz archive.tar.gz ./packages\n    print('pip installing ...')\n    !tar xfvz /kaggle/input/ultralytics-for-offline-install/archive.tar.gz\n    !pip install --no-index --find-links=./packages ultralytics\n    !rm -rf ./packages\n\nprint('time taken:', int(timer() - start_timer)//60, 'min')\nimport ultralytics\nprint('ultralytics',ultralytics.__version__)\nprint('PIP INSTALL OK!!!')\n\n#time taken: 1 min","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-05T05:42:40.722328Z","iopub.execute_input":"2025-05-05T05:42:40.722484Z","iopub.status.idle":"2025-05-05T05:44:43.994879Z","shell.execute_reply.started":"2025-05-05T05:42:40.722469Z","shell.execute_reply":"2025-05-05T05:44:43.994183Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob\nimport cv2\nimport numpy as np\nimport pandas as pd\nfrom timeit import default_timer as timer\n\nimport matplotlib\nimport matplotlib.pyplot as plt\n\nfrom ultralytics import YOLO, RTDETR\n\nimport os,sys\nsys.path.append('/kaggle/working/')\n\n\n#--- helper -------------------- \nclass dotdict(dict):\n    __setattr__ = dict.__setitem__\n    __delattr__ = dict.__delitem__\n\n    def __getattr__(self, name):\n        try:\n            return self[name]\n        except KeyError:\n            raise AttributeError(name)\n\ndef time_to_str(t, mode='min'):\n\tif mode=='min':\n\t\tt  = int(t)/60\n\t\thr = t//60\n\t\tmin = t%60\n\t\treturn '%2d hr %02d min'%(hr,min)\n\n\telif mode=='sec':\n\t\tt   = int(t)\n\t\tmin = t//60\n\t\tsec = t%60\n\t\treturn '%2d min %02d sec'%(min,sec)\n\telse:\n\t\traise NotImplementedError\n#--------------------------------\n\n\n\nprint('IMPORT OK!!!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T05:44:43.995732Z","iopub.execute_input":"2025-05-05T05:44:43.996115Z","iopub.status.idle":"2025-05-05T05:44:44.371784Z","shell.execute_reply.started":"2025-05-05T05:44:43.996093Z","shell.execute_reply":"2025-05-05T05:44:44.371093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"KAGGLE_DATA_DIR='/kaggle/input/byu-locating-bacterial-flagellar-motors-2025'\n\nMODE='submit'\n\nif MODE == 'local':\n    valid_dir = f'{KAGGLE_DATA_DIR}/train'\n    valid_id = ['tomo_00e047', 'tomo_0c3a99', 'tomo_0fe63f', 'tomo_13484c', 'tomo_0363f2', 'tomo_1446aa', 'tomo_19a313', 'tomo_1cc887', 'tomo_221a47', 'tomo_2483bb', 'tomo_2a6ca2', 'tomo_2c9da1', 'tomo_30b580', 'tomo_331130', 'tomo_378f43', 'tomo_3e7783', 'tomo_412d88', 'tomo_455dcd', 'tomo_4b124b', 'tomo_4ee35e', 'tomo_517f70', 'tomo_53e048', 'tomo_57c814', 'tomo_5dd63d', 'tomo_60ddbd', 'tomo_640a74', 'tomo_672101', 'tomo_68e123', 'tomo_6cb0f0', 'tomo_6f2c1f', 'tomo_73173f', 'tomo_79756f', 'tomo_7f0184', 'tomo_82d780', 'tomo_881d84', 'tomo_8e4f7d', 'tomo_8f4d60', 'tomo_93c0b4', 'tomo_98686a', 'tomo_99a3ce', 'tomo_9f1828', 'tomo_a1a9a3', 'tomo_a537dd', 'tomo_a910fe', 'tomo_b03f81', 'tomo_b54396', 'tomo_ba9b3d', 'tomo_be4a3a', 'tomo_c36baf', 'tomo_c7b008', 'tomo_cc65a9', 'tomo_d0aa3b', 'tomo_d26fcb', 'tomo_d6c63f', 'tomo_d9a2af', 'tomo_decb81', 'tomo_e34af8', 'tomo_e63ab4', 'tomo_ec1314', 'tomo_f2fa4a', 'tomo_f871ad', 'tomo_fc5ae4', 'tomo_003acc', 'tomo_04d42b', 'tomo_087d64', 'tomo_0c2749', 'tomo_10a3bd', 'tomo_17143f', 'tomo_1c75ac', 'tomo_221c8e', 'tomo_24a095', 'tomo_288d4f', 'tomo_2c9f35', 'tomo_307f33', 'tomo_37c426', 'tomo_3a3519', 'tomo_3e6ead', 'tomo_466489', 'tomo_4baff0', 'tomo_4e3e37', 'tomo_512f98', 'tomo_569981', 'tomo_5d01e8', 'tomo_646049', 'tomo_6607ec', 'tomo_6a6a3b', 'tomo_6e196d', 'tomo_746d88', 'tomo_7dc063', 'tomo_80bf0f', 'tomo_85708b', 'tomo_8acc4b', 'tomo_8ee8fd', 'tomo_957567', 'tomo_97876d', 'tomo_9dbc12', 'tomo_a4f419', 'tomo_ab78d0', 'tomo_aeaf51', 'tomo_b24f1a', 'tomo_b7becf', 'tomo_b98cf6', 'tomo_bb5ac1', 'tomo_be9b98', 'tomo_c3619a', 'tomo_c596be', 'tomo_c925ee', 'tomo_cae587', 'tomo_d31c96', 'tomo_d8c917', 'tomo_dbc66d', 'tomo_dfdc32', 'tomo_e32b81', 'tomo_e72e60', 'tomo_e8db69', 'tomo_ecbc12', 'tomo_f672c0', 'tomo_f8b835', 'tomo_fc1665', 'tomo_ff7c20']\n    valid_id = valid_id[:20]\nif MODE == 'submit':\n    valid_dir = f'{KAGGLE_DATA_DIR}/test'\n    valid_id = glob.glob(f'{valid_dir}/*')\n    valid_id = [f.split('/')[-1] for f in valid_id]\n\nprint('valid_id:', len(valid_id))\nprint(valid_id[:5], '...')\n\ncfg = dotdict(\n    slice_step=1,\n    batch_size=8,\n\n    #box_min_conf=0.60,\n    #box_min_conf=0.40,\n    #box_min_conf=0.70,\n\n    #box_min_conf=0.80,\n\n    box_min_conf=0.40,\n    \n    \n    box_size=24,\n    #iou_threshold=0.5,\n\n    #iou_threshold=0.4,\n     \n    #iou_threshold=0.2,\n\n    iou_threshold=0.5,\n\n    device='cuda',\n    #device='cpu',\n    checkpoint=\\\n    #'/kaggle/input/rtdetr-60e-wts/runs/detect/train/weights/best.pt'\n    #'/kaggle/input/rtdetr-100epoch-wts/last.pt'\n\n    #'/kaggle/input/rtdetr-custom-aug-60-wts/last.pt'\n    #'/kaggle/input/rtdetr-60e-wts/runs/detect/train/weights/best.pt'\n\n    '/kaggle/input/rtdetr-60e-wts/runs/detect/train/weights/best.pt'\n    \n)\n\nprint('MODE:', MODE)\nprint('SETTING OK!!!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T05:44:44.373570Z","iopub.execute_input":"2025-05-05T05:44:44.373909Z","iopub.status.idle":"2025-05-05T05:44:44.387006Z","shell.execute_reply.started":"2025-05-05T05:44:44.373888Z","shell.execute_reply":"2025-05-05T05:44:44.386214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#double gpu \n\nmodel = []\nfor i in [0,1] :\n    #m = YOLO(cfg.checkpoint)\n    #m = RTDETR(cfg.checkpoint)\n    m = RTDETR(cfg.checkpoint)\n    m.to(f'cuda:{i}')\n    m.fuse()\n    m.model.half()\n    model.append(m)\n\nprint('MODEL OK!!!')\n ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T05:47:24.034817Z","iopub.execute_input":"2025-05-05T05:47:24.035086Z","iopub.status.idle":"2025-05-05T05:47:24.062287Z","shell.execute_reply.started":"2025-05-05T05:47:24.035063Z","shell.execute_reply":"2025-05-05T05:47:24.061358Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"'''\n#m = YOLO(cfg.checkpoint)\nm = RTDETR(cfg.checkpoint)\nm.to(f'cpu')\nm.fuse()\n#m.model.half()\n\nprint('MODEL OK!!!')\n\n'''","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T05:47:24.256651Z","iopub.execute_input":"2025-05-05T05:47:24.257192Z","iopub.status.idle":"2025-05-05T05:47:24.261544Z","shell.execute_reply.started":"2025-05-05T05:47:24.257172Z","shell.execute_reply":"2025-05-05T05:47:24.260951Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# for evaluation\ndef make_truth_df(valid_id):\n    label_df = pd.read_csv(f'{KAGGLE_DATA_DIR}/train_labels.csv',\n       dtype={\n           'Number of motors': int,\n           'Array shape (axis 0)': int,\n           'Array shape (axis 1)': int,\n           'Array shape (axis 2)': int,\n           'Motor axis 0': int,\n           'Motor axis 1': int,\n           'Motor axis 2': int,\n           'Voxel spacing': float,\n       })\n\n    truth_df = []\n    for tomo_id in valid_id:\n        df = label_df[label_df['tomo_id'] == tomo_id]\n        zyx = df[['Motor axis 0','Motor axis 1','Motor axis 2']].values[0].tolist()\n        spacing = df['Voxel spacing'].values[0]\n        num_motor = df['Number of motors'].values[0]\n        truth_df.append({\n            'tomo_id': tomo_id,\n            'Voxel spacing': spacing,\n            'Motor axis 0': zyx[0],\n            'Motor axis 1': zyx[1],\n            'Motor axis 2': zyx[2],\n            'Has motor': int(num_motor>0)\n        })\n\n    truth_df = pd.DataFrame(truth_df)\n    return truth_df\n\n# inference helper\n\ndef distance_3d(d1, d2):\n    return np.sqrt((d1['z'] - d2['z']) ** 2 +\n                   (d1['y'] - d2['y']) ** 2 +\n                   (d1['x'] - d2['x']) ** 2)\n\ndef do_mns_3d(detection):\n    if not detection:\n        return []\n\n    distance_threshold = cfg.box_size * cfg.iou_threshold\n\n    # Sort by confidence (highest first)\n    detection = sorted(detection, key=lambda x: x['confidence'], reverse=True)\n\n    nms = []\n    while detection:\n        # take the detection with highest confidence\n        best_detection = detection.pop(0)\n        nms.append(best_detection)\n        detection = [d for d in detection if distance_3d(d, best_detection) > distance_threshold]\n\n    return nms\n\n\n\ndef predict_one(model, tomo_id):\n    image_file = glob.glob(f'{valid_dir}/{tomo_id}/*.jpg')\n    image_file = sorted(image_file)\n    m = cv2.imread(image_file[0], cv2.IMREAD_GRAYSCALE)\n    D = len(image_file)\n    H,W = m.shape\n\n    slice_no = np.arange(D)[::cfg.slice_step]\n    image_file = image_file[::cfg.slice_step]\n    num_file = len(image_file)\n\n    detection=[]\n    for i in range(0, num_file, cfg.batch_size):\n        batch_z = slice_no[i:i+cfg.batch_size]\n        #print(batch_z)\n\n        batch_file = image_file[i:i+cfg.batch_size]\n        result = model(batch_file, verbose=False)\n\n        for j, r in enumerate(result):\n            if len(r.boxes) > 0:\n                boxes = r.boxes\n                for k, confidence in enumerate(boxes.conf):\n                    if confidence >= cfg.box_min_conf:\n\n                        x1, y1, x2, y2 = boxes.xyxy[k].cpu().numpy()\n                        x = (x1 + x2) / 2\n                        y = (y1 + y2) / 2\n                        z = batch_z[j]\n                        # Store detection with 3D coordinates\n                        detection.append({\n                            'z': round(z),\n                            'y': round(y),\n                            'x': round(x),\n                            'confidence': float(confidence)\n                        })\n    # 3D Non-Maximum Suppression to merge nearby detections across slices\n    mns = do_mns_3d(detection)\n    mns.sort(key=lambda x: x['confidence'], reverse=True)\n\n\n    # If there are no detections, return NA values\n    if not mns:\n        return {\n            'tomo_id': tomo_id,\n            'Motor axis 0': -1,\n            'Motor axis 1': -1,\n            'Motor axis 2': -1,\n            'confidence': cfg.box_min_conf,\n        }\n    else:\n        return {\n            'tomo_id': tomo_id,\n            'Motor axis 0': mns[0]['z'],\n            'Motor axis 1': mns[0]['y'],\n            'Motor axis 2': mns[0]['x'],\n            'confidence': mns[0]['confidence'],\n        }\n\n\n\n\ndef do_predict(model, valid_id, rank):\n\n    result = []\n    total_time_taken = 0\n    for i,tomo_id in enumerate(valid_id):\n        start_timer = timer()\n\n        r = predict_one(model, tomo_id)\n        #print(r)\n        result.append(r)\n        time_taken = timer() - start_timer\n        total_time_taken += time_taken\n        print('\\r',f'rank{rank}', i, r,  time_to_str(total_time_taken, 'min'), end='')\n        if 0: #MODE=='local': #show some example\n            pass\n\n            if i ==0:\n                z = int(r['Motor axis 0'])\n                y = int(r['Motor axis 1'])\n                x = int(r['Motor axis 2'])\n                if z==-1:\n                    continue\n\n                image_file = f'{valid_dir}/{tomo_id}/slice_{z:04d}.jpg'\n                m = cv2.imread(image_file, cv2.IMREAD_GRAYSCALE)\n                \n                overlay = np.stack([m, m, m], -1)\n                cv2.circle(overlay, (x,y), 6, (0,255,255), 2)\n\n                #overlay = 255 - (255 - overlay) * (1 - p)\n                overlay = overlay.astype(np.uint8)\n                plt.imshow(overlay)\n                plt.show()\n    print('')\n    return result\n\n\n#########################################################################################################\nif 1:\n    from concurrent.futures import ThreadPoolExecutor\n    start_timer = timer()\n    N = len(valid_id)\n    with ThreadPoolExecutor(max_workers=2) as executor:\n        future0 = executor.submit(do_predict, model[0], valid_id[0::2], 0)\n        future1 = executor.submit(do_predict, model[1], valid_id[1::2], 1)\n        #future0 = executor.submit(do_predict, m, valid_id[0::2], 0)\n        #future1 = executor.submit(do_predict, m, valid_id[1::2], 1)\n        result0 = future0.result()\n        result1 = future1.result()\n    result = result0+result1\n\n    total_time_taken = timer() - start_timer\n    print('** total_time_taken:',time_to_str(total_time_taken, 'min'))\n    print('time est for 900 tomograph:', time_to_str(total_time_taken/len(valid_id)*900, 'min'))\n    print('')\n\n    result_df = pd.DataFrame(result)\n    result_df.to_csv('result.csv',index=False)\n\n    submit_df = result_df[['tomo_id','Motor axis 0','Motor axis 1','Motor axis 2']]\n    submit_df.to_csv('submission.csv',index=False)\n    print(submit_df)\n    print('SUBMIT OK !!!!!!!!!!!!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T05:47:24.282827Z","iopub.execute_input":"2025-05-05T05:47:24.283351Z","iopub.status.idle":"2025-05-05T05:47:24.309473Z","shell.execute_reply.started":"2025-05-05T05:47:24.283334Z","shell.execute_reply":"2025-05-05T05:47:24.308575Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"'''\n\nif MODE=='local':\n    from kaggle_fbeta_metric import score as compute_lb\n    result_df = pd.read_csv('result.csv')\n    submit_df = pd.read_csv('submission.csv')\n    \n    truth_df  = make_truth_df(valid_id)\n \n    lb, more = compute_lb(truth_df, submit_df, min_radius=1000, beta=2)\n    print( lb, more['fscore'], more['precision'], more['recall'])\n'''","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-05T05:47:24.309896Z","iopub.status.idle":"2025-05-05T05:47:24.310099Z","shell.execute_reply.started":"2025-05-05T05:47:24.310001Z","shell.execute_reply":"2025-05-05T05:47:24.310011Z"}},"outputs":[],"execution_count":null}]}