{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":91249,"databundleVersionId":11294684,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false},"papermill":{"default_parameters":{},"duration":62.356469,"end_time":"2025-04-01T13:50:07.995713","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-04-01T13:49:05.639244","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom IPython.display import HTML\nfrom matplotlib.animation import FuncAnimation","metadata":{"_cell_guid":"2241658b-9feb-44df-a98b-c803fd403064","_uuid":"ffa27629-7ece-4116-ba10-8420a6bdcb5c","collapsed":false,"execution":{"iopub.status.busy":"2025-04-08T10:10:02.102831Z","iopub.execute_input":"2025-04-08T10:10:02.103131Z","iopub.status.idle":"2025-04-08T10:10:03.270317Z","shell.execute_reply.started":"2025-04-08T10:10:02.103105Z","shell.execute_reply":"2025-04-08T10:10:03.269042Z"},"jupyter":{"outputs_hidden":false},"papermill":{"duration":1.625866,"end_time":"2025-04-01T13:49:10.423976","exception":false,"start_time":"2025-04-01T13:49:08.798110","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ROOT = \"D:/Workspace/Kaggle/2025_BYU/byu-locating-bacterial-flagellar-motors-2025\"\nROOT = \"/kaggle/input/byu-locating-bacterial-flagellar-motors-2025\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:50:30.349067Z","iopub.execute_input":"2025-04-05T09:50:30.349638Z","iopub.status.idle":"2025-04-05T09:50:30.354044Z","shell.execute_reply.started":"2025-04-05T09:50:30.349600Z","shell.execute_reply":"2025-04-05T09:50:30.353004Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"LABEL = os.path.join(ROOT, \"train_labels.csv\")\n\nlabel = pd.read_csv(LABEL)\nlabel.head()","metadata":{"execution":{"iopub.status.busy":"2025-04-05T09:50:30.355544Z","iopub.execute_input":"2025-04-05T09:50:30.355954Z","iopub.status.idle":"2025-04-05T09:50:30.446994Z","shell.execute_reply.started":"2025-04-05T09:50:30.355913Z","shell.execute_reply":"2025-04-05T09:50:30.445901Z"},"papermill":{"duration":0.05974,"end_time":"2025-04-01T13:49:10.487357","exception":false,"start_time":"2025-04-01T13:49:10.427617","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Image\ndef get_data(tomo_id, N=30):\n    DIR = os.path.join(ROOT, \"train\", f\"tomo_{tomo_id}\")\n    all_slices = [DIR + \"/\" + slice for slice in os.listdir(DIR)]\n    step = len(all_slices) // N\n    select_slices = all_slices[::step][:N]\n\n    # labels\n    select_labels = label[label.tomo_id == f\"tomo_{tomo_id}\"]\n\n    video_size = select_labels[[\"Array shape (axis 0)\", \"Array shape (axis 1)\", \"Array shape (axis 2)\"]].iloc[0]\n    motor_axis = select_labels[[\"Motor axis 0\", \"Motor axis 1\", \"Motor axis 2\"]].reset_index(drop=True).values\n\n    return select_slices, motor_axis, video_size\n\ndef create_slice_animation(select_slices, motor_axis, video_size, blur, figsize=(4, 4), img_size=(64, 64)):\n    N = len(select_slices)\n\n    # 원본 좌표를 리사이즈된 좌표로 변환하는 함수\n    def transform_coordinates(z, y, x, original_size, new_size):\n        z_ratio = (N - 1) / (original_size[0] - 1)\n        new_z = int(round(z * z_ratio))\n        x_ratio = new_size[0] / original_size[2]\n        y_ratio = new_size[1] / original_size[1]\n        new_x = int(round(x * x_ratio))\n        new_y = int(round(y * y_ratio))\n        return new_z, new_y, new_x\n\n    # motor_axis 좌표 변환\n    new_coords = []\n    for z, y, x in motor_axis:\n        new_z, new_y, new_x = transform_coordinates(z, y, x, video_size, img_size)\n        new_coords.append((new_z, new_y, new_x))\n\n    # 이미지 전처리 및 리사이징\n    resized_slices = []\n    for slice_img_path in select_slices:\n        img = cv2.imread(slice_img_path)\n        img = cv2.resize(img, img_size, interpolation=cv2.INTER_AREA)  # 작은 이미지에 적합한 리사이즈 방식\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        resized_slices.append(img)\n\n    # blur score 표기\n    for slice in resized_slices:\n        score = blur(slice)\n        # 이미지에 blur score 텍스트 추가\n        cv2.putText(slice, \n                   f'Blur: {score:.2f}', \n                   (10, 20),  # 텍스트 위치 (좌상단)\n                   cv2.FONT_HERSHEY_SIMPLEX,  # 폰트\n                   0.3,  # 폰트 크기\n                   (255, 255, 255),  # 텍스트 색상 (흰색)\n                   1)  # 텍스트 두께\n        \n    # Bounding box 그리기\n    box_size = 3  # 박스 크기를 더 줄이기\n    for coord in new_coords:\n        z, y, x = coord\n        for i in range(max(0, z-2), min(len(resized_slices), z+3)):  # 사용 프레임 범위를 줄이기\n            pt1 = (max(0, x-box_size), max(0, y-box_size))\n            pt2 = (min(img_size[0]-1, x+box_size), min(img_size[1]-1, y+box_size))\n            cv2.rectangle(resized_slices[i], pt1, pt2, (255, 0, 0), 1)\n\n    # 애니메이션 생성\n    fig, ax = plt.subplots(figsize=figsize, dpi=80)  # DPI 감소\n    im = ax.imshow(resized_slices[0], interpolation='lanczos')\n    ax.axis('off')\n    \n    def update(frame):\n        im.set_array(resized_slices[frame])\n        return [im]\n    \n    anim = FuncAnimation(\n        fig, \n        update, \n        frames=len(resized_slices),\n        interval=200,\n        blit=True  # blit 옵션 활성화\n    )\n    \n    plt.close()\n    return anim\n","metadata":{"execution":{"iopub.status.busy":"2025-04-05T09:50:30.449570Z","iopub.execute_input":"2025-04-05T09:50:30.449886Z","iopub.status.idle":"2025-04-05T09:50:30.468236Z","shell.execute_reply.started":"2025-04-05T09:50:30.449860Z","shell.execute_reply":"2025-04-05T09:50:30.466755Z"},"papermill":{"duration":0.017938,"end_time":"2025-04-01T13:49:10.508800","exception":false,"start_time":"2025-04-01T13:49:10.490862","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Blur\ndef load_image(path, img_size):\n    img = cv2.imread(path)\n    img = cv2.resize(img, img_size, interpolation=cv2.INTER_AREA)  # 작은 이미지에 적합한 리사이즈 방식\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    return img\n\ndef plot_blur(blur, tomo_id, img_size=(64, 64)):\n    PATH = os.path.join(ROOT, \"train\", f\"tomo_{tomo_id}\")\n\n    slices = [os.path.join(PATH, slice) for slice in sorted(os.listdir(PATH))]\n    blurriness = [blur(load_image(slice, img_size)) for slice in slices]\n    \n    plt.plot(blurriness)\n\n    # motor point\n    z_axis = label[label.tomo_id == f\"tomo_{tomo_id}\"][\"Motor axis 0\"].values\n    for point in z_axis:\n        slice_index = int(point)\n        plt.scatter(x=slice_index, y=blurriness[slice_index], c=\"red\")\n\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:50:30.470072Z","iopub.execute_input":"2025-04-05T09:50:30.470576Z","iopub.status.idle":"2025-04-05T09:50:30.494985Z","shell.execute_reply.started":"2025-04-05T09:50:30.470535Z","shell.execute_reply":"2025-04-05T09:50:30.493696Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def laplacian(image):\n    return cv2.Laplacian(image, cv2.CV_64F).var()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:50:30.496162Z","iopub.execute_input":"2025-04-05T09:50:30.496580Z","iopub.status.idle":"2025-04-05T09:50:30.518263Z","shell.execute_reply.started":"2025-04-05T09:50:30.496548Z","shell.execute_reply":"2025-04-05T09:50:30.516874Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tomo_id = \"00e047\"\nselect_slices, motor_axis, video_size = get_data(tomo_id)\n\nanim = create_slice_animation(select_slices, motor_axis, video_size, blur=laplacian, img_size=(128, 128))\ndisplay(HTML(anim.to_jshtml()))\n\nplot_blur(laplacian, tomo_id, img_size=(128, 128))","metadata":{"execution":{"iopub.status.busy":"2025-04-05T09:50:30.519419Z","iopub.execute_input":"2025-04-05T09:50:30.519876Z","iopub.status.idle":"2025-04-05T09:50:41.975989Z","shell.execute_reply.started":"2025-04-05T09:50:30.519825Z","shell.execute_reply":"2025-04-05T09:50:41.974841Z"},"papermill":{"duration":4.217648,"end_time":"2025-04-01T13:49:14.730315","exception":false,"start_time":"2025-04-01T13:49:10.512667","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tomo_id = \"00e463\"\nselect_slices, motor_axis, video_size = get_data(tomo_id)\n\nanim = create_slice_animation(select_slices, motor_axis, video_size, blur=laplacian, img_size=(128, 128))\ndisplay(HTML(anim.to_jshtml()))\n\nplot_blur(laplacian, tomo_id, img_size=(128, 128))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:50:41.976956Z","iopub.execute_input":"2025-04-05T09:50:41.977268Z","iopub.status.idle":"2025-04-05T09:50:59.854839Z","shell.execute_reply.started":"2025-04-05T09:50:41.977241Z","shell.execute_reply":"2025-04-05T09:50:59.853575Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tomo_id = \"01a877\"\nselect_slices, motor_axis, video_size = get_data(tomo_id)\n\nanim = create_slice_animation(select_slices, motor_axis, video_size, blur=laplacian, img_size=(128, 128))\ndisplay(HTML(anim.to_jshtml()))\n\nplot_blur(laplacian, tomo_id, img_size=(128, 128))","metadata":{"papermill":{"duration":0.132743,"end_time":"2025-04-01T13:50:07.139001","exception":false,"start_time":"2025-04-01T13:50:07.006258","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:50:59.856078Z","iopub.execute_input":"2025-04-05T09:50:59.856506Z","iopub.status.idle":"2025-04-05T09:51:11.188848Z","shell.execute_reply.started":"2025-04-05T09:50:59.856460Z","shell.execute_reply":"2025-04-05T09:51:11.187486Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tomo_id = \"02862f\"\nselect_slices, motor_axis, video_size = get_data(tomo_id)\n\nanim = create_slice_animation(select_slices, motor_axis, video_size, blur=laplacian, img_size=(128, 128))\ndisplay(HTML(anim.to_jshtml()))\n\nplot_blur(laplacian, tomo_id, img_size=(128, 128))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:51:11.190044Z","iopub.execute_input":"2025-04-05T09:51:11.190407Z","iopub.status.idle":"2025-04-05T09:51:23.337556Z","shell.execute_reply.started":"2025-04-05T09:51:11.190369Z","shell.execute_reply":"2025-04-05T09:51:23.336479Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tomo_id = \"a537dd\"\nselect_slices, motor_axis, video_size = get_data(tomo_id)\n\nanim = create_slice_animation(select_slices, motor_axis, video_size, blur=laplacian, img_size=(128, 128))\ndisplay(HTML(anim.to_jshtml()))\n\nplot_blur(laplacian, tomo_id, img_size=(128, 128))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:51:23.338555Z","iopub.execute_input":"2025-04-05T09:51:23.338883Z","iopub.status.idle":"2025-04-05T09:51:33.084648Z","shell.execute_reply.started":"2025-04-05T09:51:23.338840Z","shell.execute_reply":"2025-04-05T09:51:33.083225Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def analyze_blur(blur, tomo_id, img_size, z_threshold=3):\n    PATH = os.path.join(ROOT, \"train\", f\"tomo_{tomo_id}\")\n\n    slices = [os.path.join(PATH, slice) for slice in sorted(os.listdir(PATH))]\n    blurriness = [blur(load_image(slice, img_size)) for slice in slices]\n\n    min_blur = min(blurriness)\n    max_blur = max(blurriness)\n    mean_blur = sum(blurriness) / len(blurriness)\n\n    z_axis = label[label.tomo_id == f\"tomo_{tomo_id}\"][\"Motor axis 0\"].values[0]\n    z_axis = int(z_axis)\n    keypoint_blur = blurriness[z_axis]\n    keypoint_near_blur = sum(blurriness[z_axis - z_threshold : z_axis + z_threshold + 1]) / (z_threshold * 2 + 1)\n\n    keypoint_near_percentile = sum(1 for x in blurriness if x <= keypoint_near_blur) / len(blurriness) * 100\n\n    return min_blur, max_blur, mean_blur, keypoint_blur, keypoint_near_blur, keypoint_near_percentile","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:51:33.085856Z","iopub.execute_input":"2025-04-05T09:51:33.086180Z","iopub.status.idle":"2025-04-05T09:51:33.094283Z","shell.execute_reply.started":"2025-04-05T09:51:33.086153Z","shell.execute_reply":"2025-04-05T09:51:33.092581Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tomo_id_with_motors = set(label[label[\"Number of motors\"] > 0].tomo_id.values)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:51:33.097916Z","iopub.execute_input":"2025-04-05T09:51:33.098411Z","iopub.status.idle":"2025-04-05T09:51:33.121850Z","shell.execute_reply.started":"2025-04-05T09:51:33.098362Z","shell.execute_reply":"2025-04-05T09:51:33.120520Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\n\n# Create empty lists to store results\nmin_blurs = []\nmax_blurs = []\nmean_blurs = []\nkeypoint_blurs = []\nkeypoint_near_blurs = []\nkeypoint_near_percentiles = []\ntomo_ids = []\n\n# Analyze blur for each tomo_id\nfor tomo_id in tqdm(tomo_id_with_motors):\n    # Extract just the ID part without 'tomo_' prefix\n    id_only = tomo_id.replace('tomo_', '')\n    \n    # Get blur analysis results\n    min_b, max_b, mean_b, key_b, key_near_b, key_near_p = analyze_blur(laplacian, id_only, (128, 128))\n    \n    # Append results to lists\n    min_blurs.append(min_b)\n    max_blurs.append(max_b) \n    mean_blurs.append(mean_b)\n    keypoint_blurs.append(key_b)\n    keypoint_near_blurs.append(key_near_b)\n    keypoint_near_percentiles.append(key_near_p)\n    tomo_ids.append(tomo_id)\n\n# Create DataFrame\nblur_df = pd.DataFrame({\n    'tomo_id': tomo_ids,\n    'min_blur': min_blurs,\n    'max_blur': max_blurs,\n    'mean_blur': mean_blurs,\n    'keypoint_blur': keypoint_blurs,\n    'keypoint_near_blur': keypoint_near_blurs,\n    \"keypoint_near_percentiles\": keypoint_near_percentiles\n})","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T09:51:33.123487Z","iopub.execute_input":"2025-04-05T09:51:33.123942Z","iopub.status.idle":"2025-04-05T10:39:06.339403Z","shell.execute_reply.started":"2025-04-05T09:51:33.123907Z","shell.execute_reply":"2025-04-05T10:39:06.336992Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"blur_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T10:39:06.341605Z","iopub.execute_input":"2025-04-05T10:39:06.342130Z","iopub.status.idle":"2025-04-05T10:39:06.359611Z","shell.execute_reply.started":"2025-04-05T10:39:06.342055Z","shell.execute_reply":"2025-04-05T10:39:06.358660Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"blur_df.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T10:39:06.361164Z","iopub.execute_input":"2025-04-05T10:39:06.361599Z","iopub.status.idle":"2025-04-05T10:39:06.420533Z","shell.execute_reply.started":"2025-04-05T10:39:06.361553Z","shell.execute_reply":"2025-04-05T10:39:06.419325Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"x = blur_df[\"keypoint_near_blur\"].argmin()\ntomo_id = tomo_ids[x].rsplit(\"_\", 1)[-1]\n\nselect_slices, motor_axis, video_size = get_data(tomo_id)\n\nanim = create_slice_animation(select_slices, motor_axis, video_size, blur=laplacian, img_size=(128, 128))\ndisplay(HTML(anim.to_jshtml()))\n\nplot_blur(laplacian, tomo_id, img_size=(128, 128))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T10:39:06.421784Z","iopub.execute_input":"2025-04-05T10:39:06.422159Z","iopub.status.idle":"2025-04-05T10:39:15.999433Z","shell.execute_reply.started":"2025-04-05T10:39:06.422122Z","shell.execute_reply":"2025-04-05T10:39:15.997616Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"x = blur_df[\"keypoint_near_percentiles\"].argmin()\ntomo_id = tomo_ids[x].rsplit(\"_\", 1)[-1]\n\nselect_slices, motor_axis, video_size = get_data(tomo_id)\n\nanim = create_slice_animation(select_slices, motor_axis, video_size, blur=laplacian, img_size=(128, 128))\ndisplay(HTML(anim.to_jshtml()))\n\nplot_blur(laplacian, tomo_id, img_size=(128, 128))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T10:39:16.000765Z","iopub.execute_input":"2025-04-05T10:39:16.001224Z","iopub.status.idle":"2025-04-05T10:39:23.154834Z","shell.execute_reply.started":"2025-04-05T10:39:16.001180Z","shell.execute_reply":"2025-04-05T10:39:23.153614Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print((blur_df[\"keypoint_near_blur\"] > 400).mean())\nprint((blur_df[\"keypoint_near_blur\"] > 700).mean())\nprint((blur_df[\"keypoint_near_blur\"] > 800).mean())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T10:50:07.885579Z","iopub.execute_input":"2025-04-05T10:50:07.885950Z","iopub.status.idle":"2025-04-05T10:50:07.894422Z","shell.execute_reply.started":"2025-04-05T10:50:07.885921Z","shell.execute_reply":"2025-04-05T10:50:07.893076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"(blur_df[\"keypoint_near_percentiles\"] > 0.68).mean()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T10:48:39.279541Z","iopub.execute_input":"2025-04-05T10:48:39.279909Z","iopub.status.idle":"2025-04-05T10:48:39.286908Z","shell.execute_reply.started":"2025-04-05T10:48:39.279879Z","shell.execute_reply":"2025-04-05T10:48:39.285869Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def select_frame(blur, tomo_id, img_size, z_threshold=3, quantile=0.68):\n    PATH = os.path.join(ROOT, \"train\", f\"tomo_{tomo_id}\")\n\n    slices = [os.path.join(PATH, slice) for slice in sorted(os.listdir(PATH))]\n    blurriness = [blur(load_image(slice, img_size)) for slice in slices]\n\n    threshold = np.quantile(blurriness, quantile)\n    select = sum([1 for x in blurriness if x > threshold])\n\n    return len(slices), select","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T10:52:20.214424Z","iopub.execute_input":"2025-04-05T10:52:20.214877Z","iopub.status.idle":"2025-04-05T10:52:20.222075Z","shell.execute_reply.started":"2025-04-05T10:52:20.214841Z","shell.execute_reply":"2025-04-05T10:52:20.220650Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"total = 0\nreduce = 0\n\nfor tomo_id in tqdm(tomo_id_with_motors):\n    tomo_id = tomo_ids[x].rsplit(\"_\", 1)[-1]\n    framesize, select = select_frame(laplacian, tomo_id, (128, 128))\n    total += framesize\n    reduce += select\n\nprint(total, reduce)\nprint(f\"Select {reduce/total * 100:.2f} frames\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T10:54:23.605995Z","iopub.execute_input":"2025-04-05T10:54:23.606429Z","iopub.status.idle":"2025-04-05T11:16:38.056516Z","shell.execute_reply.started":"2025-04-05T10:54:23.606396Z","shell.execute_reply":"2025-04-05T11:16:38.055484Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Try with smaller image size\nfrom tqdm import tqdm\n\n# Create empty lists to store results\nmin_blurs = []\nmax_blurs = []\nmean_blurs = []\nkeypoint_blurs = []\nkeypoint_near_blurs = []\nkeypoint_near_percentiles = []\ntomo_ids = []\n\n# Analyze blur for each tomo_id\nfor tomo_id in tqdm(tomo_id_with_motors):\n    # Extract just the ID part without 'tomo_' prefix\n    id_only = tomo_id.replace('tomo_', '')\n    \n    # Get blur analysis results\n    min_b, max_b, mean_b, key_b, key_near_b, key_near_p = analyze_blur(laplacian, id_only, (64, 64))\n    \n    # Append results to lists\n    min_blurs.append(min_b)\n    max_blurs.append(max_b) \n    mean_blurs.append(mean_b)\n    keypoint_blurs.append(key_b)\n    keypoint_near_blurs.append(key_near_b)\n    keypoint_near_percentiles.append(key_near_p)\n    tomo_ids.append(tomo_id)\n\n# Create DataFrame\nblur_df = pd.DataFrame({\n    'tomo_id': tomo_ids,\n    'min_blur': min_blurs,\n    'max_blur': max_blurs,\n    'mean_blur': mean_blurs,\n    'keypoint_blur': keypoint_blurs,\n    'keypoint_near_blur': keypoint_near_blurs,\n    \"keypoint_near_percentiles\": keypoint_near_percentiles\n})","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T11:19:02.021566Z","iopub.execute_input":"2025-04-05T11:19:02.021983Z","iopub.status.idle":"2025-04-05T12:02:52.200225Z","shell.execute_reply.started":"2025-04-05T11:19:02.021952Z","shell.execute_reply":"2025-04-05T12:02:52.197481Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"blur_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T12:21:33.397489Z","iopub.execute_input":"2025-04-05T12:21:33.399562Z","iopub.status.idle":"2025-04-05T12:21:33.444714Z","shell.execute_reply.started":"2025-04-05T12:21:33.399422Z","shell.execute_reply":"2025-04-05T12:21:33.443357Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"blur_df.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T12:29:09.455120Z","iopub.execute_input":"2025-04-05T12:29:09.455646Z","iopub.status.idle":"2025-04-05T12:29:09.496519Z","shell.execute_reply.started":"2025-04-05T12:29:09.455598Z","shell.execute_reply":"2025-04-05T12:29:09.495285Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print((blur_df[\"keypoint_near_blur\"] > 400).mean())\nprint((blur_df[\"keypoint_near_blur\"] > 700).mean())\nprint((blur_df[\"keypoint_near_blur\"] > 800).mean())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-05T12:29:21.245959Z","iopub.execute_input":"2025-04-05T12:29:21.246364Z","iopub.status.idle":"2025-04-05T12:29:21.257174Z","shell.execute_reply.started":"2025-04-05T12:29:21.246329Z","shell.execute_reply":"2025-04-05T12:29:21.255824Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"(blur_df[\"keypoint_near_percentiles\"] > 0.61).mean()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"total = 0\nreduce = 0\n\nfor tomo_id in tqdm(tomo_id_with_motors):\n    tomo_id = tomo_ids[x].rsplit(\"_\", 1)[-1]\n    framesize, select = select_frame(laplacian, tomo_id, (128, 128), quantile=0.61)\n    total += framesize\n    reduce += select\n\nprint(total, reduce)\nprint(f\"Select {reduce/total * 100:.2f} frames\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}