{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"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":"gpu","dataSources":[{"sourceId":91249,"databundleVersionId":11294684,"sourceType":"competition"},{"sourceId":226864880,"sourceType":"kernelVersion"}],"dockerImageVersionId":30919,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","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":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-07T19:49:28.560748Z","iopub.execute_input":"2025-04-07T19:49:28.561114Z","iopub.status.idle":"2025-04-07T19:50:22.496853Z","shell.execute_reply.started":"2025-04-07T19:49:28.561087Z","shell.execute_reply":"2025-04-07T19:50:22.495792Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"##\n# Main configuration\nDATA_PATH = \"/kaggle/input/byu-locating-bacterial-flagellar-motors-2025/\"\nTRAIN_DIR = os.path.join(DATA_PATH, \"train\")\nTRAIN_CSV = os.path.join(DATA_PATH, \"train_labels.csv\")\nBASE_OUTPUT_DIR = \"/kaggle/working/classified_motor_slices_clean\"\nTRUST_RANGE = 5  # Only next 5 slices\nos.makedirs(BASE_OUTPUT_DIR, exist_ok=True)\n# Load dataset\nlabels_df = pd.read_csv(TRAIN_CSV)\ntomo_with_motors = labels_df['tomo_id'].unique()\n\nprint(f\"🔍 Number of tomograms containing motors: {len(tomo_with_motors)}\")\n\ndef rotate_image_and_coords(image, angle, x, y, img_width, img_height):\n    \"\"\"Rotate the image and calculate the new coordinates of the motor based on the angle\"\"\"\n    if angle == 0:\n        rotated_image = image.copy()\n        new_x, new_y = x, y\n    elif angle == 90:\n        rotated_image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)\n        new_x, new_y = img_width - y, x  # Rotation formula for 90 degrees\n    elif angle == 180:\n        rotated_image = cv2.rotate(image, cv2.ROTATE_180)\n        new_x, new_y = img_width - x, img_height - y  # Rotation formula for 180 degrees\n    elif angle == 270:\n        rotated_image = cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE)\n        new_x, new_y = y, img_width - x  # Rotation formula for 270 degrees\n\n    return rotated_image, new_x, new_y\n\ndef organize_clean_slices():\n    processed_slices = set()\n    \n    for tomo_id in tqdm(tomo_with_motors, desc=\"🧪 Processing tomograms\"):\n        tomo_output_dir = os.path.join(BASE_OUTPUT_DIR, tomo_id)\n        os.makedirs(tomo_output_dir, exist_ok=True)\n        \n        tomo_path = os.path.join(TRAIN_DIR, tomo_id)\n        all_slices = sorted([f for f in os.listdir(tomo_path) if f.endswith('.jpg')])\n        total_slices = len(all_slices)\n        \n        tomo_motors = labels_df[labels_df['tomo_id'] == tomo_id]\n\n        for _, motor in tomo_motors.iterrows():\n            z, y, x = int(motor['Motor axis 0']), int(motor['Motor axis 1']), int(motor['Motor axis 2'])\n            is_valid = all(coord >= 0 for coord in [z, y, x])\n            \n            if is_valid:\n                z_start = z\n                z_end = min(total_slices - 1, z + TRUST_RANGE)\n                coords = (z, y, x)\n            else:\n                # ➕ مرکز 10 اسلایس وسط\n                mid = total_slices // 2\n                half_range = 5\n                z_start = max(0, mid - half_range)\n                z_end = min(total_slices - 1, mid + half_range - 1)\n                coords = (-1, -1, -1)\n            \n            for z_idx in range(z_start, z_end + 1):\n                slice_name = f\"slice_{z_idx:04d}.jpg\"\n                src_path = os.path.join(tomo_path, slice_name)\n                unique_key = f\"{tomo_id}_{slice_name}_{coords}\"\n                \n                if os.path.exists(src_path) and unique_key not in processed_slices:\n                    rel_pos = z_idx - z if is_valid else 0\n                    dest_name = f\"motor{coords[0]}-{coords[1]}-{coords[2]}_slice{z_idx:04d}_pos{rel_pos:+d}.jpg\"\n                    dest_path = os.path.join(tomo_output_dir, dest_name)\n                    shutil.copy2(src_path, dest_path)\n\n                    img = cv2.imread(src_path)\n                    img_height, img_width = img.shape[:2]\n\n                    for angle in [0, 90, 180, 270]:\n                        if is_valid:\n                            rotated_img, new_x, new_y = rotate_image_and_coords(\n                                img, angle, x, y, img_width, img_height\n                            )\n                            ax1, ax2 = new_y, new_x\n                        else:\n                            rotated_img = rotate_image_and_coords(img, angle, 0, 0, img_width, img_height)[0]\n                            ax1 = ax2 = -1\n\n                        rotated_dest_name = f\"motor{coords[0]}-{coords[1]}-{coords[2]}_slice{z_idx:04d}_rot{angle}.jpg\"\n                        rotated_dest_path = os.path.join(tomo_output_dir, rotated_dest_name)\n                        cv2.imwrite(rotated_dest_path, rotated_img)\n\n                        rotated_entry = {\n                            \"tomo_id\": tomo_id,\n                            \"slice_number\": z_idx,\n                            \"rotation_angle\": angle,\n                            \"motor_axis_0\": coords[0],\n                            \"motor_axis_1\": ax1,\n                            \"motor_axis_2\": ax2\n                        }\n                        rotated_df = pd.DataFrame([rotated_entry])\n                        rotated_csv_path = os.path.join(tomo_output_dir, \"rotated_motor_coordinates.csv\")\n\n                        if not os.path.exists(rotated_csv_path):\n                            rotated_df.to_csv(rotated_csv_path, index=False)\n                        else:\n                            rotated_df.to_csv(rotated_csv_path, mode='a', header=False, index=False)\n\n                    processed_slices.add(unique_key)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-07T19:53:06.028092Z","iopub.execute_input":"2025-04-07T19:53:06.028547Z","iopub.status.idle":"2025-04-07T19:53:06.113296Z","shell.execute_reply.started":"2025-04-07T19:53:06.028516Z","shell.execute_reply":"2025-04-07T19:53:06.112123Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# Main configuration\nDATA_PATH = \"/kaggle/input/byu-locating-bacterial-flagellar-motors-2025/\"\nTRAIN_DIR = os.path.join(DATA_PATH, \"train\")\nTRAIN_CSV = os.path.join(DATA_PATH, \"train_labels.csv\")\nBASE_OUTPUT_DIR = \"/kaggle/working/classified_motor_slices_clean\"\nTRUST_RANGE = 5  # Only next 5 slices\nos.makedirs(BASE_OUTPUT_DIR, exist_ok=True)\n# Load dataset\nlabels_df = pd.read_csv(TRAIN_CSV)\ntomo_with_motors = labels_df['tomo_id'].unique()\n\nprint(f\"🔍 Number of tomograms containing motors: {len(tomo_with_motors)}\")\n\ndef rotate_image_and_coords(image, angle, x, y, img_width, img_height):\n    \"\"\"Rotate the image and calculate the new coordinates of the motor based on the angle\"\"\"\n    if angle == 0:\n        rotated_image = image.copy()\n        new_x, new_y = x, y\n    elif angle == 90:\n        rotated_image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)\n        new_x, new_y = img_width - y, x  # Rotation formula for 90 degrees\n    elif angle == 180:\n        rotated_image = cv2.rotate(image, cv2.ROTATE_180)\n        new_x, new_y = img_width - x, img_height - y  # Rotation formula for 180 degrees\n    elif angle == 270:\n        rotated_image = cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE)\n        new_x, new_y = y, img_width - x  # Rotation formula for 270 degrees\n\n    return rotated_image, new_x, new_y\n\ndef organize_clean_slices():\n    \"\"\"Organize slices and rotate images while saving the new motor coordinates\"\"\"\n    processed_slices = set()  # To avoid duplication\n    \n    for tomo_id in tqdm(tomo_with_motors, desc=\"🧪 Processing tomograms\"):\n        # Create output folder for the current tomogram\n        tomo_output_dir = os.path.join(BASE_OUTPUT_DIR, tomo_id)\n        os.makedirs(tomo_output_dir, exist_ok=True)\n        \n        tomo_path = os.path.join(TRAIN_DIR, tomo_id)\n        all_slices = sorted([f for f in os.listdir(tomo_path) if f.endswith('.jpg')])\n        total_slices = len(all_slices)\n        \n        # Get valid motors for this tomogram (positive coordinates only)\n        tomo_motors = labels_df[(labels_df['tomo_id'] == tomo_id) & \n                               (labels_df['Motor axis 0'] >= 0) &\n                               (labels_df['Motor axis 1'] >= 0) &\n                               (labels_df['Motor axis 2'] >= 0َ\n        \n        for _, motor in tomo_motors.iterrows():\n            z_center = int(motor['Motor axis 0'])\n            y_center = int(motor['Motor axis 1'])\n            x_center = int(motor['Motor axis 2'])\n            \n            # Slice range (only next 5 slices after motor)\n            z_start = z_center\n            z_end = min(total_slices - 1, z_center + TRUST_RANGE)\n            \n            for z in range(z_start, z_end + 1):\n                slice_name = f\"slice_{z:04d}.jpg\"\n                src_path = os.path.join(tomo_path, slice_name)\n                \n                # Unique key to avoid duplicate processing\n                unique_key = f\"{tomo_id}_{slice_name}\"\n                \n                if os.path.exists(src_path) and unique_key not in processed_slices:\n                    # Output filename with motor information\n                    rel_pos = z - z_center  # Relative position (0 to +5)\n                    dest_name = f\"motor{z_center}-{y_center}-{x_center}_slice{z:04d}_pos{rel_pos:+d}.jpg\"\n                    dest_path = os.path.join(tomo_output_dir, dest_name)\n                    \n                    # Copy the original image\n                    shutil.copy2(src_path, dest_path)\n                    \n                    # Read and rotate image at different angles\n                    img = cv2.imread(src_path)\n                    img_height, img_width = img.shape[:2]\n                    \n                    for angle in [0, 90, 180, 270]:\n                        rotated_img, new_x, new_y = rotate_image_and_coords(\n                            img, angle, x_center, y_center, img_width, img_height\n                        )\n                        \n                        # Save rotated image\n                        rotated_dest_name = f\"motor{z_center}-{y_center}-{x_center}_slice{z:04d}_rot{angle}.jpg\"\n                        rotated_dest_path = os.path.join(tomo_output_dir, rotated_dest_name)\n                        cv2.imwrite(rotated_dest_path, rotated_img)\n                        \n                        # Save new coordinates into CSV\n                        rotated_entry = {\n                            \"tomo_id\": tomo_id,\n                            \"slice_number\": z,\n                            \"rotation_angle\": angle,\n                            \"motor_axis_0\": z_center,\n                            \"motor_axis_1\": new_y,\n                            \"motor_axis_2\": new_x\n                        }\n                        rotated_df = pd.DataFrame([rotated_entry])\n                        \n                        # Append or create CSV file for rotated coordinates\n                        rotated_csv_path = os.path.join(tomo_output_dir, \"rotated_motor_coordinates.csv\")\n                        if not os.path.exists(rotated_csv_path):\n                            rotated_df.to_csv(rotated_csv_path, index=False)\n                        else:\n                            rotated_df.to_csv(rotated_csv_path, mode='a', header=False, index=False)\n                    \n                    processed_slices.add(unique_key)\n\n# Execute the function\nprint(\"🔍 Organizing slices and rotating images...\")\norganize_clean_slices()\n\n# Final result summary\nprint(f\"\\n✅ Organization completed successfully!\")\nprint(f\"📁 Main output directory: {BASE_OUTPUT_DIR}\")\nprint(f\"📊 Number of processed tomograms: {len(os.listdir(BASE_OUTPUT_DIR))}\")\n\n# Show example folder structure\nsample_tomo = os.listdir(BASE_OUTPUT_DIR)[0] if os.listdir(BASE_OUTPUT_DIR) else None\nif sample_tomo:\n    sample_path = os.path.join(BASE_OUTPUT_DIR, sample_tomo)\n    print(f\"\\n📂 Sample tomogram folder structure:\")\n    print(f\"{sample_tomo}/\")\n    print(\"│\")\n    sample_files = os.listdir(sample_path)[:3]  # Show first 3 files\n    for f in sample_files:\n        print(f\"├── {f}\")\n    if len(os.listdir(sample_path)) > 3:\n        print(f\"└── ... ({len(os.listdir(sample_path)) - 3} more files)\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T16:56:53.230996Z","iopub.execute_input":"2025-04-06T16:56:53.231338Z","iopub.status.idle":"2025-04-06T16:58:34.902727Z","shell.execute_reply.started":"2025-04-06T16:56:53.231309Z","shell.execute_reply":"2025-04-06T16:58:34.901722Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\ndef delete_pos_zero_images(tomo_output_dir):\n    \"\"\"Delete images with _pos+0 in the filename\"\"\"\n    for root, dirs, files in os.walk(tomo_output_dir):\n        for file in files:\n            if \"_pos\" in file:  # If the filename contains _pos+0\n                file_path = os.path.join(root, file)\n                os.remove(file_path)  # Delete the file\n                print(f\"🗑️ Deleted: {file_path}\")\n\n# Path to the tomogram folder\nBASE_OUTPUT_DIR = \"/kaggle/working/classified_motor_slices_clean\"\n\n# Delete images with _pos+0 from each tomogram folder\nfor tomo_id in os.listdir(BASE_OUTPUT_DIR):\n    tomo_output_dir = os.path.join(BASE_OUTPUT_DIR, tomo_id)\n    if os.path.isdir(tomo_output_dir):\n        print(f\"🔍 Processing folder: {tomo_id}\")\n        delete_pos_zero_images(tomo_output_dir)\n\nprint(\"✅ All images with _pos+0 have been deleted.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T16:58:38.597597Z","iopub.execute_input":"2025-04-06T16:58:38.597915Z","iopub.status.idle":"2025-04-06T16:58:39.349627Z","shell.execute_reply.started":"2025-04-06T16:58:38.597891Z","shell.execute_reply":"2025-04-06T16:58:39.348804Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Paths for the input and output folders\nBASE_OUTPUT_DIR = \"/kaggle/working/classified_motor_slices_clean\"\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images\"  # Final folder to store images\nos.makedirs(FINAL_IMAGES_DIR, exist_ok=True)\n\n# Function to move images and add the tomogram name to the file name\ndef move_images_to_final_folder_with_tomo_id():\n    for tomo_id in os.listdir(BASE_OUTPUT_DIR):\n        tomo_output_dir = os.path.join(BASE_OUTPUT_DIR, tomo_id)\n        if os.path.isdir(tomo_output_dir):\n            for file in os.listdir(tomo_output_dir):\n                if file.endswith('.jpg'):\n                    # Rename the file by adding the tomo_id\n                    new_file_name = f\"{tomo_id}_{file}\"\n                    src_path = os.path.join(tomo_output_dir, file)\n                    dest_path = os.path.join(FINAL_IMAGES_DIR, new_file_name)\n                    \n                    # Move the file with the new name\n                    shutil.copy2(src_path, dest_path)  \n                    print(f\"📂 Image transferred: {new_file_name}\")\n\n# Run the function to move the images\nmove_images_to_final_folder_with_tomo_id()\n\nprint(f\"✅ All images have been moved to the folder {FINAL_IMAGES_DIR}.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T16:58:49.153441Z","iopub.execute_input":"2025-04-06T16:58:49.153750Z","iopub.status.idle":"2025-04-06T16:58:59.483164Z","shell.execute_reply.started":"2025-04-06T16:58:49.153723Z","shell.execute_reply":"2025-04-06T16:58:59.482088Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Paths for the directories\nBASE_OUTPUT_DIR = \"/kaggle/working/classified_motor_slices_clean\"\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates.xlsx\"  # Path for the final Excel file\n\n# List to store the data\nall_data = []\n\n# Read information from each tomogram\nfor tomo_id in os.listdir(BASE_OUTPUT_DIR):\n    tomo_output_dir = os.path.join(BASE_OUTPUT_DIR, tomo_id)\n    rotated_csv_path = os.path.join(tomo_output_dir, \"rotated_motor_coordinates.csv\")\n    \n    if os.path.exists(rotated_csv_path):\n        # Load the tomogram coordinates table\n        rotated_df = pd.read_csv(rotated_csv_path)\n        \n        # Add a column for the tomogram name\n        rotated_df['tomo_id'] = tomo_id\n        \n        # Add the data to the list\n        all_data.append(rotated_df)\n\n# Combine all data into one dataframe\nfinal_df = pd.concat(all_data, ignore_index=True)\n\n# Save the data to an Excel file\nfinal_df.to_excel(FINAL_EXCEL_PATH, index=False)\n\nprint(f\"✅ The final Excel file has been created with the name {FINAL_EXCEL_PATH}.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T16:59:35.129739Z","iopub.execute_input":"2025-04-06T16:59:35.130166Z","iopub.status.idle":"2025-04-06T16:59:37.069681Z","shell.execute_reply.started":"2025-04-06T16:59:35.130129Z","shell.execute_reply":"2025-04-06T16:59:37.068939Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images\"\n\n# Count the total number of images\ntotal_images = len([f for f in os.listdir(FINAL_IMAGES_DIR) if f.endswith('.jpg')])\n\nprint(f\"🔢 Total number of images: {total_images}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T15:47:26.707490Z","iopub.execute_input":"2025-04-06T15:47:26.707947Z","iopub.status.idle":"2025-04-06T15:47:26.720975Z","shell.execute_reply.started":"2025-04-06T15:47:26.707914Z","shell.execute_reply":"2025-04-06T15:47:26.720294Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path to the final Excel file\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates.xlsx\"\n\n# Load the Excel file\nfinal_df = pd.read_excel(FINAL_EXCEL_PATH)\n\n# Total number of rows\ntotal_rows = len(final_df)\n\nprint(f\"🔢 Total number of rows in the Excel file: {total_rows}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T15:47:31.130816Z","iopub.execute_input":"2025-04-06T15:47:31.131112Z","iopub.status.idle":"2025-04-06T15:47:32.143477Z","shell.execute_reply.started":"2025-04-06T15:47:31.131089Z","shell.execute_reply":"2025-04-06T15:47:32.142660Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images\"\n\n# Path to the final Excel file\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates.xlsx\"\n\n# Load the Excel file\nfinal_df = pd.read_excel(FINAL_EXCEL_PATH)\n\n# Find all the images in the folder\nall_images = sorted([f for f in os.listdir(FINAL_IMAGES_DIR) if f.endswith('.jpg')])\n\n# Third image\nthird_image = all_images[2]  # Third image (index 2)\n\n# Extract tomo_id, slice number, and rotation angle from the third image file name\ntomo_id = third_image.split('_')[0] + \"_\" + third_image.split('_')[1]  # Example: \"tomo_00e047\"\nslice_number = int(third_image.split('_')[3][5:])  # Example: \"slice0169\" -> 169\n\n# Extract the rotation angle (we need to convert \"rot270\" to 270)\nrotation_angle_str = third_image.split('_')[4]  # Example: \"rot270.jpg\"\nrotation_angle = int(rotation_angle_str[3:6])  # Extract number from \"rot270\" -> 270\n\n# Print the third image's name and extracted information\nprint(f\"📸 Third image: {third_image}\")\nprint(f\"tomo_id: {tomo_id}\")\nprint(f\"Slice number: {slice_number}\")\nprint(f\"Rotation angle: {rotation_angle} degrees\")\n\n# Filter the Excel data for the specific image\nimage_data = final_df[(final_df['tomo_id'] == tomo_id) & \n                      (final_df['slice_number'] == slice_number) & \n                      (final_df['rotation_angle'] == rotation_angle)]\n\n# Print the motor coordinates for the third image\nmotor_x = image_data['motor_axis_2'].values[0]  # X from column motor_axis_2\nmotor_y = image_data['motor_axis_1'].values[0]  # Y from column motor_axis_1\nmotor_z = image_data['motor_axis_0'].values[0]  # Z from column motor_axis_0\nprint(f\"🔍 Motor coordinates for the third image: X={motor_x}, Y={motor_y}, Z={motor_z}\")\n\n# Load the image\nimage_path = os.path.join(FINAL_IMAGES_DIR, third_image)\nimg = Image.open(image_path)\n\n# Draw a red circle at the motor coordinates\ndraw = ImageDraw.Draw(img)\ncircle_radius = 10  # Circle radius\n\n# Draw the red circle (at coordinates X, Y)\ndraw.ellipse([motor_x - circle_radius, motor_y - circle_radius,\n              motor_x + circle_radius, motor_y + circle_radius],\n             outline=\"red\", width=3)\n\n# Display the image using matplotlib\nplt.imshow(img)\nplt.axis('off')  # Hide x and y axes\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T15:47:36.702986Z","iopub.execute_input":"2025-04-06T15:47:36.703281Z","iopub.status.idle":"2025-04-06T15:47:37.812865Z","shell.execute_reply.started":"2025-04-06T15:47:36.703260Z","shell.execute_reply":"2025-04-06T15:47:37.812002Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images\"\n\n# Path to the final Excel file\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates.xlsx\"\n\n# Load the Excel file\nfinal_df = pd.read_excel(FINAL_EXCEL_PATH)\n\n# Find the third image in the folder\nall_images = sorted([f for f in os.listdir(FINAL_IMAGES_DIR) if f.endswith('.jpg')])\n\n# The third image\nthird_image = all_images[6602]  # Third image (index 6602)\n\n# Extract tomo_id, slice number, and rotation angle from the third image's filename\ntomo_id = third_image.split('_')[0] + \"_\" + third_image.split('_')[1]  # Example: \"tomo_00e047\"\nslice_number = int(third_image.split('_')[3][5:])  # Example: \"slice0169\" -> 169\n\n# Extract rotation angle (we need to convert \"rot270\" to 270)\nrotation_angle_str = third_image.split('_')[4]  # Example: \"rot270.jpg\"\nrotation_angle_str = rotation_angle_str.replace('.jpg', '')  # Remove the .jpg extension\nrotation_angle = int(rotation_angle_str[3:])  # Extract the number from \"rot270\" -> 270\n\n\n# Print the third image name and extracted information\nprint(f\"📸 Third image: {third_image}\")\nprint(f\"tomo_id: {tomo_id}\")\nprint(f\"Slice number: {slice_number}\")\nprint(f\"Rotation angle: {rotation_angle} degrees\")\n\n# Filter the Excel data for the specific image\nimage_data = final_df[(final_df['tomo_id'] == tomo_id) & \n                      (final_df['slice_number'] == slice_number) & \n                      (final_df['rotation_angle'] == rotation_angle)]\n\n# Print the motor coordinates for the third image\nmotor_x = image_data['motor_axis_2'].values[0]  # X from motor_axis_2 column\nmotor_y = image_data['motor_axis_1'].values[0]  # Y from motor_axis_1 column\nmotor_z = image_data['motor_axis_0'].values[0]  # Z from motor_axis_0 column\nprint(f\"🔍 Motor coordinates for the third image: X={motor_x}, Y={motor_y}, Z={motor_z}\")\n\n# Load the image\nimage_path = os.path.join(FINAL_IMAGES_DIR, third_image)\nimg = Image.open(image_path)\n\n# Draw a red circle at the motor coordinates\ndraw = ImageDraw.Draw(img)\ncircle_radius = 10  # Circle radius\n\n# Draw a red circle (at X, Y coordinates)\ndraw.ellipse([motor_x - circle_radius, motor_y - circle_radius,\n              motor_x + circle_radius, motor_y + circle_radius],\n             outline=\"red\", width=3)\n\n# Display the image using matplotlib\nplt.imshow(img)\nplt.axis('off')  # Remove x and y axes\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T15:47:49.274534Z","iopub.execute_input":"2025-04-06T15:47:49.274840Z","iopub.status.idle":"2025-04-06T15:47:50.271112Z","shell.execute_reply.started":"2025-04-06T15:47:49.274817Z","shell.execute_reply":"2025-04-06T15:47:50.270280Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path to the folder you want to delete\nfolder_path = \"/kaggle/working/classified_motor_slices_clean\"\n\n# Check if the folder exists and delete it along with all files and subfolders\nif os.path.exists(folder_path) and os.path.isdir(folder_path):\n    shutil.rmtree(folder_path)  # Delete the folder and all its contents\n    print(f\"✅ Folder '{folder_path}' and all its files have been successfully deleted.\")\nelse:\n    print(f\"⚠️ Folder '{folder_path}' not found.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T16:59:51.738789Z","iopub.execute_input":"2025-04-06T16:59:51.739390Z","iopub.status.idle":"2025-04-06T16:59:53.065374Z","shell.execute_reply.started":"2025-04-06T16:59:51.739344Z","shell.execute_reply":"2025-04-06T16:59:53.064480Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images\"\n\n# Path to the final Excel file\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates.xlsx\"\n\n# Path to the folder to save YOLO label files\nYOLO_LABELS_DIR = \"/kaggle/working/yolo_labels\"\n\n# Create folder if it doesn't exist\nif not os.path.exists(YOLO_LABELS_DIR):\n    os.makedirs(YOLO_LABELS_DIR)\n\n# Load the Excel file\nfinal_df = pd.read_excel(FINAL_EXCEL_PATH)\n\n# Find all images in the folder\nall_images = sorted([f for f in os.listdir(FINAL_IMAGES_DIR) if f.endswith('.jpg')])\n\nfor image_name in all_images:\n    # Extract tomo_id, slice number, and rotation angle from the image file name\n    tomo_id = image_name.split('_')[0] + \"_\" + image_name.split('_')[1]\n    slice_number = int(image_name.split('_')[3][5:])\n    \n    # Correctly extract the rotation angle\n    rotation_angle_str = image_name.split('_')[4]\n    rotation_angle_str = rotation_angle_str.replace('.jpg', '')  # Remove the .jpg extension\n    rotation_angle = ''.join(filter(str.isdigit, rotation_angle_str))  # Extract only digits\n    rotation_angle = int(rotation_angle)  # Convert to integer\n\n    # Filter the Excel data for the specific image\n    image_data = final_df[(final_df['tomo_id'] == tomo_id) & \n                          (final_df['slice_number'] == slice_number) & \n                          (final_df['rotation_angle'] == rotation_angle)]\n\n    # Extract motor coordinates for the image\n    motor_x = image_data['motor_axis_2'].values[0]  # X in the motor_axis_2 column\n    motor_y = image_data['motor_axis_1'].values[0]  # Y in the motor_axis_1 column\n    motor_z = image_data['motor_axis_0'].values[0]  # Z in the motor_axis_0 column\n\n    # Load the image to get the dimensions\n    image_path = os.path.join(FINAL_IMAGES_DIR, image_name)\n    img = Image.open(image_path)\n\n    # Image dimensions (width and height)\n    image_width, image_height = img.size\n\n    # Assuming the real dimensions of the object are as follows (here 80 is used as the object size)\n    real_width = 80  # Actual object width (in pixels)\n    real_height = 80  # Actual object height (in pixels)\n\n    # Normalize the width and height\n    width = real_width / image_width\n    height = real_height / image_height\n\n    # Normalize the motor coordinates (motor_x, motor_y)\n    x_center = motor_x / image_width\n    y_center = motor_y / image_height\n\n    # Path to the YOLO label file for saving the text file with the same name as the image\n    label_file_path = os.path.join(YOLO_LABELS_DIR, f\"{os.path.splitext(image_name)[0]}.txt\")\n\n    # Save the data in YOLO format\n    with open(label_file_path, 'w') as f:\n        f.write(f\"0 {x_center} {y_center} {width} {height}\\n\")\n\n    # Display the image dimensions and normalized coordinates\n    print(f\"Image dimensions: {image_width}x{image_height}\")\n    print(f\"Coordinates (X, Y): ({motor_x}, {motor_y})\")\n    print(f\"Normalized Bounding Box: x_center={x_center}, y_center={y_center}, width={width}, height={height}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T16:59:56.420868Z","iopub.execute_input":"2025-04-06T16:59:56.421200Z","iopub.status.idle":"2025-04-06T17:00:22.094261Z","shell.execute_reply.started":"2025-04-06T16:59:56.421172Z","shell.execute_reply":"2025-04-06T17:00:22.093592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images\"\n\n# Path to the folder for YOLO label files\nYOLO_LABELS_DIR = \"/kaggle/working/yolo_labels\"\n\n# File for the fifth image (index 4)\nimage_name = sorted([f for f in os.listdir(FINAL_IMAGES_DIR) if f.endswith('.jpg')])[10]  # Fifth image\n\n# Load the image\nimage_path = os.path.join(FINAL_IMAGES_DIR, image_name)\nimg = Image.open(image_path)\n\n# Path to the corresponding YOLO text file for the image\nlabel_file_path = os.path.join(YOLO_LABELS_DIR, f\"{os.path.splitext(image_name)[0]}.txt\")\n\n# Read the YOLO file data\nwith open(label_file_path, 'r') as f:\n    label_data = f.readlines()\n\n# Extract normalized coordinates and dimensions from the YOLO file\nfor label in label_data:\n    parts = label.split()\n    class_id = int(parts[0])  # Class ID (0 for motor)\n    x_center = float(parts[1])  # Normalized X center\n    y_center = float(parts[2])  # Normalized Y center\n    width = float(parts[3])  # Normalized width\n    height = float(parts[4])  # Normalized height\n\n    # Image dimensions\n    image_width, image_height = img.size\n\n    # Convert normalized coordinates to pixels\n    x_center_pixel = x_center * image_width\n    y_center_pixel = y_center * image_height\n    width_pixel = width * image_width\n    height_pixel = height * image_height\n\n    # Calculate the coordinates of the bounding box (for drawing)\n    left = x_center_pixel - width_pixel / 2\n    top = y_center_pixel - height_pixel / 2\n    right = x_center_pixel + width_pixel / 2\n    bottom = y_center_pixel + height_pixel / 2\n\n    # Draw the bounding box on the image\n    draw = ImageDraw.Draw(img)\n    draw.rectangle([left, top, right, bottom], outline=\"red\", width=3)\n\n# Display the image with the bounding box\nplt.imshow(img)\nplt.axis('off')  # Remove x and y axis\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T17:01:51.784962Z","iopub.execute_input":"2025-04-06T17:01:51.785366Z","iopub.status.idle":"2025-04-06T17:01:52.059680Z","shell.execute_reply.started":"2025-04-06T17:01:51.785337Z","shell.execute_reply":"2025-04-06T17:01:52.058671Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n\n# Path to input data\nIMAGES_DIR = \"/kaggle/working/all_motor_images\"\nLABELS_DIR = \"/kaggle/working/yolo_labels\"\n\n# Path for the final YOLO structure\nBASE_DIR = \"/kaggle/working/motor_dataset\"\nos.makedirs(BASE_DIR, exist_ok=True)\n\n# Create the necessary directories for train and val\nfor folder in ['images/train', 'images/val', 'labels/train', 'labels/val']:\n    os.makedirs(os.path.join(BASE_DIR, folder), exist_ok=True)\n\n# Get all filenames (without extension)\nimage_files = sorted([f for f in os.listdir(IMAGES_DIR) if f.endswith('.jpg')])\nimage_names = [os.path.splitext(f)[0] for f in image_files]\n\n# Split into train and val (90% train, 10% val)\ntrain_names, val_names = train_test_split(image_names, test_size=0.1, random_state=42)\n\n# Function to copy image and label files\ndef copy_data(file_list, split):\n    for name in file_list:\n        img_src = os.path.join(IMAGES_DIR, name + \".jpg\")\n        label_src = os.path.join(LABELS_DIR, name + \".txt\")\n\n        img_dst = os.path.join(BASE_DIR, f\"images/{split}\", name + \".jpg\")\n        label_dst = os.path.join(BASE_DIR, f\"labels/{split}\", name + \".txt\")\n\n        if os.path.exists(img_src) and os.path.exists(label_src):\n            shutil.copyfile(img_src, img_dst)\n            shutil.copyfile(label_src, label_dst)\n\n# Copy the files\ncopy_data(train_names, \"train\")\ncopy_data(val_names, \"val\")\n\n# Create a dataset.yaml file for YOLOv8\nyaml_path = os.path.join(BASE_DIR, \"dataset.yaml\")\nwith open(yaml_path, \"w\") as f:\n    f.write(\n        f\"path: {BASE_DIR}\\n\"\n        f\"train: images/train\\n\"\n        f\"val: images/val\\n\"\n        f\"names: ['motor']\\n\"\n    )\n\nprint(\"✅ Directories have been created and data has been split.\")\nprint(f\"📁 Final path: {BASE_DIR}\")\nprint(f\"📝 The YAML file is also located at: {yaml_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T17:02:12.454631Z","iopub.execute_input":"2025-04-06T17:02:12.455044Z","iopub.status.idle":"2025-04-06T17:02:38.132875Z","shell.execute_reply.started":"2025-04-06T17:02:12.455004Z","shell.execute_reply":"2025-04-06T17:02:38.131950Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path of folders and files\nall_motor_images_path = \"/kaggle/working/all_motor_images\"\nfinal_motor_coordinates_path = \"/kaggle/working/final_motor_coordinates.xlsx\"\n\n# Delete the all_motor_images folder and all its contents\nif os.path.exists(all_motor_images_path) and os.path.isdir(all_motor_images_path):\n    shutil.rmtree(all_motor_images_path)\n    print(f\"✅ Folder '{all_motor_images_path}' and all its contents were successfully deleted.\")\nelse:\n    print(f\"⚠️ Folder '{all_motor_images_path}' not found.\")\n\n# Delete the final_motor_coordinates.xlsx file\nif os.path.exists(final_motor_coordinates_path):\n    os.remove(final_motor_coordinates_path)\n    print(f\"✅ File '{final_motor_coordinates_path}' was successfully deleted.\")\nelse:\n    print(f\"⚠️ File '{final_motor_coordinates_path}' not found.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T17:02:42.739915Z","iopub.execute_input":"2025-04-06T17:02:42.740241Z","iopub.status.idle":"2025-04-06T17:02:56.363361Z","shell.execute_reply.started":"2025-04-06T17:02:42.740215Z","shell.execute_reply":"2025-04-06T17:02:56.362473Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path to the yaml file containing dataset information\ndataset_yaml_path = \"/kaggle/working/motor_dataset/dataset.yaml\"\n\n# Train YOLOv8 model\nmodel = YOLO('yolov8n.yaml')  # Base YOLOv8 model (you can also use other models like 'yolov8s.yaml')\n\n# Train the model using the YAML file\nmodel.train(data=dataset_yaml_path, epochs=50, imgsz=640, batch=16, name='motor_model')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T17:02:59.880152Z","iopub.execute_input":"2025-04-06T17:02:59.880479Z","iopub.status.idle":"2025-04-06T19:04:04.859480Z","shell.execute_reply.started":"2025-04-06T17:02:59.880454Z","shell.execute_reply":"2025-04-06T19:04:04.858452Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Load the trained model\nmodel = YOLO(\"runs/detect/motor_model/weights/best.pt\")\n\n# Run evaluation (val) on the validation data defined in dataset.yaml\nresults = model.val(data=\"/kaggle/working/motor_dataset/dataset.yaml\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T19:04:31.975279Z","iopub.execute_input":"2025-04-06T19:04:31.975747Z","iopub.status.idle":"2025-04-06T19:04:45.068417Z","shell.execute_reply.started":"2025-04-06T19:04:31.975709Z","shell.execute_reply":"2025-04-06T19:04:45.066643Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(results.results_dict)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T19:04:51.570695Z","iopub.execute_input":"2025-04-06T19:04:51.571070Z","iopub.status.idle":"2025-04-06T19:04:51.576441Z","shell.execute_reply.started":"2025-04-06T19:04:51.571028Z","shell.execute_reply":"2025-04-06T19:04:51.575619Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\nfor fname in [\"results.png\", \"confusion_matrix.png\", \"precision_recall_curve.png\"]:\n    fpath = os.path.join(results.save_dir, fname)\n    if os.path.exists(fpath):\n        img = Image.open(fpath)\n        plt.figure(figsize=(8, 6))\n        plt.imshow(img)\n        plt.axis('off')\n        plt.title(fname)\n        plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T19:05:03.412630Z","iopub.execute_input":"2025-04-06T19:05:03.413151Z","iopub.status.idle":"2025-04-06T19:05:04.212077Z","shell.execute_reply.started":"2025-04-06T19:05:03.413109Z","shell.execute_reply":"2025-04-06T19:05:04.210907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\npaths_to_delete = [\n    \"/kaggle/working/motor_dataset\",\n    \"/kaggle/working/yolo_labels\",\n    \"/kaggle/working/all_motor_images\",\n    \"/kaggle/working/final_motor_coordinates.xlsx\"\n]\n\nfor path in paths_to_delete:\n    if os.path.exists(path):\n        if os.path.isfile(path):\n            os.remove(path)\n            print(f\"📄 File deleted: {path}\")\n        else:\n            shutil.rmtree(path)\n            print(f\"📁 Folder deleted: {path}\")\n    else:\n        print(f\"⚠️ Not found: {path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T19:06:12.975670Z","iopub.execute_input":"2025-04-06T19:06:12.976023Z","iopub.status.idle":"2025-04-06T19:06:12.981831Z","shell.execute_reply.started":"2025-04-06T19:06:12.975989Z","shell.execute_reply":"2025-04-06T19:06:12.981020Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"import torch\n\n# Set random seed for reproducibility\nnp.random.seed(42)\ntorch.manual_seed(42)\n\n# Define paths for the test data and submission\ndata_path = \"/kaggle/input/byu-locating-bacterial-flagellar-motors-2025/\"\ntest_dir = os.path.join(data_path, \"test\")\nsubmission_path = \"/kaggle/working/submission.csv\"\n\n# Path to the best trained model (adjust if necessary)\nmodel_path=\"/kaggle/working/runs/detect/motor_model/weights/best.pt\"\n\n# Define detection and processing parameters\nCONFIDENCE_THRESHOLD = 0.45\nMAX_DETECTIONS_PER_TOMO = 3\nNMS_IOU_THRESHOLD = 0.2\nCONCENTRATION = 1  # Process a fraction of slices for fast submission\n\n# GPU profiling context manager for timing\nclass GPUProfiler:\n    def __init__(self, name):\n        self.name = name\n        self.start_time = None\n        \n    def __enter__(self):\n        if torch.cuda.is_available():\n            torch.cuda.synchronize()\n        self.start_time = time.time()\n        return self\n        \n    def __exit__(self, *args):\n        if torch.cuda.is_available():\n            torch.cuda.synchronize()\n        elapsed = time.time() - self.start_time\n        print(f\"[PROFILE] {self.name}: {elapsed:.3f}s\")\n\n# Set device and dynamic batch size\ndevice = 'cuda:0' if torch.cuda.is_available() else 'cpu'\nBATCH_SIZE = 8\nif device.startswith('cuda'):\n    torch.backends.cudnn.benchmark = True\n    torch.backends.cudnn.deterministic = False\n    torch.backends.cuda.matmul.allow_tf32 = True\n    torch.backends.cudnn.allow_tf32 = True\n    gpu_name = torch.cuda.get_device_name(0)\n    gpu_mem = torch.cuda.get_device_properties(0).total_memory / 1e9\n    print(f\"Using GPU: {gpu_name} with {gpu_mem:.2f} GB memory\")\n    free_mem = gpu_mem - torch.cuda.memory_allocated(0) / 1e9\n    BATCH_SIZE = max(8, min(32, int(free_mem * 4)))\n    print(f\"Dynamic batch size set to {BATCH_SIZE} based on {free_mem:.2f}GB free memory\")\nelse:\n    print(\"GPU not available, using CPU\")\n    BATCH_SIZE = 4","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T19:15:31.546535Z","iopub.execute_input":"2025-04-06T19:15:31.546897Z","iopub.status.idle":"2025-04-06T19:15:31.556756Z","shell.execute_reply.started":"2025-04-06T19:15:31.546870Z","shell.execute_reply":"2025-04-06T19:15:31.555888Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def normalize_slice(slice_data):\n    \"\"\"\n    Normalize slice data using the 2nd and 98th percentiles.\n    \"\"\"\n    p2 = np.percentile(slice_data, 2)\n    p98 = np.percentile(slice_data, 98)\n    clipped_data = np.clip(slice_data, p2, p98)\n    normalized = 255 * (clipped_data - p2) / (p98 - p2)\n    return np.uint8(normalized)\n\ndef preload_image_batch(file_paths):\n    \"\"\"Preload a batch of images to CPU memory.\"\"\"\n    images = []\n    for path in file_paths:\n        img = cv2.imread(path)\n        if img is None:\n            img = np.array(Image.open(path))\n        images.append(img)\n    return images\n\ndef perform_3d_nms(detections, iou_threshold):\n    \"\"\"\n    Perform 3D Non-Maximum Suppression on detections to merge nearby motors.\n    \"\"\"\n    if not detections:\n        return []\n    \n    detections = sorted(detections, key=lambda x: x['confidence'], reverse=True)\n    final_detections = []\n    def distance_3d(d1, d2):\n        return np.sqrt((d1['z'] - d2['z'])**2 + (d1['y'] - d2['y'])**2 + (d1['x'] - d2['x'])**2)\n    \n    box_size = 24\n    distance_threshold = box_size * iou_threshold\n    \n    while detections:\n        best_detection = detections.pop(0)\n        final_detections.append(best_detection)\n        detections = [d for d in detections if distance_3d(d, best_detection) > distance_threshold]\n    \n    return final_detections\n\ndef process_tomogram(tomo_id, model, index=0, total=1):\n    \"\"\"\n    Process a single tomogram and return the most confident motor detection.\n    \"\"\"\n    print(f\"Processing tomogram {tomo_id} ({index}/{total})\")\n    tomo_dir = os.path.join(test_dir, tomo_id)\n    slice_files = sorted([f for f in os.listdir(tomo_dir) if f.endswith('.jpg')])\n    \n    selected_indices = np.linspace(0, len(slice_files)-1, int(len(slice_files) * CONCENTRATION))\n    selected_indices = np.round(selected_indices).astype(int)\n    slice_files = [slice_files[i] for i in selected_indices]\n    \n    print(f\"Processing {len(slice_files)} out of {len(os.listdir(tomo_dir))} slices (CONCENTRATION={CONCENTRATION})\")\n    all_detections = []\n    \n    if device.startswith('cuda'):\n        streams = [torch.cuda.Stream() for _ in range(min(4, BATCH_SIZE))]\n    else:\n        streams = [None]\n    \n    next_batch_thread = None\n    next_batch_images = None\n    \n    for batch_start in range(0, len(slice_files), BATCH_SIZE):\n        if next_batch_thread is not None:\n            next_batch_thread.join()\n            next_batch_images = None\n            \n        batch_end = min(batch_start + BATCH_SIZE, len(slice_files))\n        batch_files = slice_files[batch_start:batch_end]\n        \n        next_batch_start = batch_end\n        next_batch_end = min(next_batch_start + BATCH_SIZE, len(slice_files))\n        next_batch_files = slice_files[next_batch_start:next_batch_end] if next_batch_start < len(slice_files) else []\n        if next_batch_files:\n            next_batch_paths = [os.path.join(tomo_dir, f) for f in next_batch_files]\n            next_batch_thread = threading.Thread(target=preload_image_batch, args=(next_batch_paths,))\n            next_batch_thread.start()\n        else:\n            next_batch_thread = None\n        \n        sub_batches = np.array_split(batch_files, len(streams))\n        for i, sub_batch in enumerate(sub_batches):\n            if len(sub_batch) == 0:\n                continue\n            stream = streams[i % len(streams)]\n            with torch.cuda.stream(stream) if stream and device.startswith('cuda') else nullcontext():\n                sub_batch_paths = [os.path.join(tomo_dir, slice_file) for slice_file in sub_batch]\n                sub_batch_slice_nums = [int(slice_file.split('_')[1].split('.')[0]) for slice_file in sub_batch]\n                with GPUProfiler(f\"Inference batch {i+1}/{len(sub_batches)}\"):\n                    sub_results = model(sub_batch_paths, verbose=False)\n                for j, result in enumerate(sub_results):\n                    if len(result.boxes) > 0:\n                        for box_idx, confidence in enumerate(result.boxes.conf):\n                            if confidence >= CONFIDENCE_THRESHOLD:\n                                x1, y1, x2, y2 = result.boxes.xyxy[box_idx].cpu().numpy()\n                                x_center = (x1 + x2) / 2\n                                y_center = (y1 + y2) / 2\n                                all_detections.append({\n                                    'z': round(sub_batch_slice_nums[j]),\n                                    'y': round(y_center),\n                                    'x': round(x_center),\n                                    'confidence': float(confidence)\n                                })\n        if device.startswith('cuda'):\n            torch.cuda.synchronize()\n    \n    if next_batch_thread is not None:\n        next_batch_thread.join()\n    \n    final_detections = perform_3d_nms(all_detections, NMS_IOU_THRESHOLD)\n    final_detections.sort(key=lambda x: x['confidence'], reverse=True)\n    \n    if not final_detections:\n        return {'tomo_id': tomo_id, 'Motor axis 0': -1, 'Motor axis 1': -1, 'Motor axis 2': -1}\n    \n    best_detection = final_detections[0]\n    return {\n        'tomo_id': tomo_id,\n        'Motor axis 0': round(best_detection['z']),\n        'Motor axis 1': round(best_detection['y']),\n        'Motor axis 2': round(best_detection['x'])\n    }\n\ndef debug_image_loading(tomo_id):\n    \"\"\"\n    Debug function to test image loading methods.\n    \"\"\"\n    tomo_dir = os.path.join(test_dir, tomo_id)\n    slice_files = sorted([f for f in os.listdir(tomo_dir) if f.endswith('.jpg')])\n    if not slice_files:\n        print(f\"No image files found in {tomo_dir}\")\n        return\n        \n    print(f\"Found {len(slice_files)} image files in {tomo_dir}\")\n    sample_file = slice_files[len(slice_files)//2]\n    img_path = os.path.join(tomo_dir, sample_file)\n    \n    try:\n        img_pil = Image.open(img_path)\n        print(f\"PIL Image shape: {np.array(img_pil).shape}, dtype: {np.array(img_pil).dtype}\")\n        img_cv2 = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)\n        print(f\"OpenCV Image shape: {img_cv2.shape}, dtype: {img_cv2.dtype}\")\n        img_rgb = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)\n        print(f\"OpenCV RGB Image shape: {img_rgb.shape}, dtype: {img_rgb.dtype}\")\n        print(\"Image loading successful!\")\n    except Exception as e:\n        print(f\"Error loading image {img_path}: {e}\")\n        \n    try:\n        test_model = YOLO(model_path)\n        test_results = test_model([img_path], verbose=False)\n        print(\"YOLO model successfully processed the test image\")\n    except Exception as e:\n        print(f\"Error with YOLO processing: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T19:15:35.890420Z","iopub.execute_input":"2025-04-06T19:15:35.890846Z","iopub.status.idle":"2025-04-06T19:15:35.920550Z","shell.execute_reply.started":"2025-04-06T19:15:35.890811Z","shell.execute_reply":"2025-04-06T19:15:35.919651Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from concurrent.futures import ThreadPoolExecutor\nimport threading\n\ndef generate_submission():\n    \"\"\"\n    Main function to generate the submission file.\n    \"\"\"\n    test_tomos = sorted([d for d in os.listdir(test_dir) if os.path.isdir(os.path.join(test_dir, d))])\n    total_tomos = len(test_tomos)\n    print(f\"Found {total_tomos} tomograms in test directory\")\n    \n    if test_tomos:\n        debug_image_loading(test_tomos[0])\n    \n    if torch.cuda.is_available():\n        torch.cuda.empty_cache()\n    \n    print(f\"Loading YOLO model from {model_path}\")\n    model = YOLO(model_path)\n    model.to(device)\n    if device.startswith('cuda'):\n        model.fuse()\n        if torch.cuda.get_device_capability(0)[0] >= 7:\n            model.model.half()\n            print(\"Using half precision (FP16) for inference\")\n    \n    results = []\n    motors_found = 0\n    \n    with ThreadPoolExecutor(max_workers=1) as executor:\n        future_to_tomo = {}\n        for i, tomo_id in enumerate(test_tomos, 1):\n            future = executor.submit(process_tomogram, tomo_id, model, i, total_tomos)\n            future_to_tomo[future] = tomo_id\n        \n        for future in future_to_tomo:\n            tomo_id = future_to_tomo[future]\n            try:\n                if torch.cuda.is_available():\n                    torch.cuda.empty_cache()\n                result = future.result()\n                results.append(result)\n                has_motor = not pd.isna(result['Motor axis 0'])\n                if has_motor:\n                    motors_found += 1\n                    print(f\"Motor found in {tomo_id} at position: z={result['Motor axis 0']}, y={result['Motor axis 1']}, x={result['Motor axis 2']}\")\n                else:\n                    print(f\"No motor detected in {tomo_id}\")\n                print(f\"Current detection rate: {motors_found}/{len(results)} ({motors_found/len(results)*100:.1f}%)\")\n            except Exception as e:\n                print(f\"Error processing {tomo_id}: {e}\")\n                results.append({'tomo_id': tomo_id, 'Motor axis 0': -1, 'Motor axis 1': -1, 'Motor axis 2': -1})\n    \n    submission_df = pd.DataFrame(results)\n    submission_df = submission_df[['tomo_id', 'Motor axis 0', 'Motor axis 1', 'Motor axis 2']]\n    submission_df.to_csv(submission_path, index=False)\n    \n    print(f\"\\nSubmission complete!\")\n    print(f\"Motors detected: {motors_found}/{total_tomos} ({motors_found/total_tomos*100:.1f}%)\")\n    print(f\"Submission saved to: {submission_path}\")\n    print(\"\\nSubmission preview:\")\n    print(submission_df.head())\n    return submission_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T19:18:33.806992Z","iopub.execute_input":"2025-04-06T19:18:33.807336Z","iopub.status.idle":"2025-04-06T19:18:33.819078Z","shell.execute_reply.started":"2025-04-06T19:18:33.807311Z","shell.execute_reply":"2025-04-06T19:18:33.818119Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import time\n\nif __name__ == \"__main__\":\n    start_time = time.time()\n    submission = generate_submission()\n    elapsed = time.time() - start_time\n    print(f\"\\nTotal execution time: {elapsed:.2f} seconds ({elapsed/60:.2f} minutes)\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-06T19:18:38.174307Z","iopub.execute_input":"2025-04-06T19:18:38.174646Z","iopub.status.idle":"2025-04-06T19:19:26.718273Z","shell.execute_reply.started":"2025-04-06T19:18:38.174621Z","shell.execute_reply":"2025-04-06T19:19:26.717344Z"}},"outputs":[],"execution_count":null}]}