{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","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":11315004,"sourceType":"datasetVersion","datasetId":6959173},{"sourceId":226864880,"sourceType":"kernelVersion"},{"sourceId":227202990,"sourceType":"kernelVersion"}],"dockerImageVersionId":30919,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":8023.127866,"end_time":"2025-04-06T22:52:35.438582","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-04-06T20:38:52.310716","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!tar xfvz /kaggle/input/ultralytics-offlineinstall-yolo12-weights/archive.tar.gz\n#!tar xfvz /kaggle/input/ultralytics-for-offline-instal/archive.tar.gz\n!pip install --no-index --find-links=./packages ultralytics\n!rm -rf ./packages\n","metadata":{"papermill":{"duration":55.000457,"end_time":"2025-04-06T20:39:50.271183","exception":false,"start_time":"2025-04-06T20:38:55.270726","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:05:49.324327Z","iopub.execute_input":"2025-05-01T19:05:49.324651Z","iopub.status.idle":"2025-05-01T19:06:39.483079Z","shell.execute_reply.started":"2025-05-01T19:05:49.324622Z","shell.execute_reply":"2025-05-01T19:06:39.482139Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#اصلاح شارپ\nimport os\nimport shutil\nimport pandas as pd\nfrom tqdm import tqdm\nimport cv2\nimport numpy as np\nfrom PIL import Image, ImageDraw\nimport matplotlib.pyplot as plt\nimport random\nfrom sklearn.model_selection import train_test_split\nfrom ultralytics import YOLO\n# Main settings\n\nDATA_PATH = \"/kaggle/input/cryoet-flagellar-motors-dataset/\"\nTRAIN_DIR = os.path.join(DATA_PATH, \"jpgs\")\nTRAIN_CSV = os.path.join(DATA_PATH, \"labels.csv\")\nBASE_OUTPUT_DIR = \"/kaggle/working/classified_motor_slices_clean\"\nos.makedirs(BASE_OUTPUT_DIR, exist_ok=True)\n\n# Load data\nlabels_df = pd.read_csv(TRAIN_CSV)\ntomo_with_motors = labels_df['tomo_id'].unique()\n\nprint(f\"🔍 Number of tomograms with motors: {len(tomo_with_motors)}\")\n\ndef organize_clean_slices():\n    \"\"\"Organize slices by keeping only the exact slice where the motor is located\"\"\"\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 this 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\n        # Get motors for this tomogram with valid (non-negative) coordinates\n        tomo_motors = labels_df[(labels_df['tomo_id'] == tomo_id) &\n                                (labels_df['z'] >= 0) &\n                                (labels_df['x'] >= 0) &\n                                (labels_df['y'] >= 0)]\n\n        for _, motor in tomo_motors.iterrows():\n            z_center = int(motor['z'])\n            y_center = int(motor['y'])\n            x_center = int(motor['x'])  # ✅ اصلاح شد (قبلاً y اشتباهی جای x بود)\n\n            # Build the slice file name\n            slice_name = f\"slice_{z_center:04d}.jpg\"\n            src_path = os.path.join(tomo_path, slice_name)\n\n            unique_key = f\"{tomo_id}_{slice_name}\"\n\n            if os.path.exists(src_path) and unique_key not in processed_slices:\n                rel_pos = 0  # فقط همون اسلایس\n                dest_name = f\"motor{z_center}-{y_center}-{x_center}_slice{z_center:04d}_pos{rel_pos:+d}.jpg\"\n                dest_path = os.path.join(tomo_output_dir, dest_name)\n\n                shutil.copy2(src_path, dest_path)\n                processed_slices.add(unique_key)\n\n# Run the function\nprint(\"🔍 Organizing slices (only next 5 slices from motor)...\")\norganize_clean_slices()\n\n# Show final result\nprint(f\"\\n✅ Organization completed successfully!\")\nprint(f\"📁 Main output folder: {BASE_OUTPUT_DIR}\")\nprint(f\"📊 Number of processed tomograms: {len(os.listdir(BASE_OUTPUT_DIR))}\")\n\n# Show sample structure of one tomogram folder\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-05-01T19:06:39.484427Z","iopub.execute_input":"2025-05-01T19:06:39.484737Z","iopub.status.idle":"2025-05-01T19:07:13.379914Z","shell.execute_reply.started":"2025-05-01T19:06:39.484702Z","shell.execute_reply":"2025-05-01T19:07:13.379094Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# Main configuration\nDATA_PATH = \"/kaggle/input/cryoet-flagellar-motors-dataset/\"\nTRAIN_DIR = os.path.join(DATA_PATH, \"jpgs\")\nTRAIN_CSV = os.path.join(DATA_PATH, \"labels.csv\")\nBASE_OUTPUT_DIR = \"/kaggle/working/classified_motor_slices_clean\"\nTRUST_RANGE = 0  # Only next 5 slices\nos.makedirs(BASE_OUTPUT_DIR, exist_ok=True)\n\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['z'] >= 0) &\n                               (labels_df['y'] >= 0) &\n                               (labels_df['x'] >= 0)]\n        \n        for _, motor in tomo_motors.iterrows():\n            z_center = int(motor['z'])\n            y_center = int(motor['y'])\n            x_center = int(motor['x'])\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":{"papermill":{"duration":0.015722,"end_time":"2025-04-06T20:40:35.116202","exception":false,"start_time":"2025-04-06T20:40:35.100480","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:07:13.381409Z","iopub.execute_input":"2025-05-01T19:07:13.381643Z","iopub.status.idle":"2025-05-01T19:07:41.708927Z","shell.execute_reply.started":"2025-05-01T19:07:13.381623Z","shell.execute_reply":"2025-05-01T19:07:41.707960Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\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-05-01T19:07:41.710444Z","iopub.execute_input":"2025-05-01T19:07:41.710735Z","iopub.status.idle":"2025-05-01T19:07:42.331439Z","shell.execute_reply.started":"2025-05-01T19:07:41.710709Z","shell.execute_reply":"2025-05-01T19:07:42.330724Z"}},"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-05-01T19:07:42.332208Z","iopub.execute_input":"2025-05-01T19:07:42.332476Z","iopub.status.idle":"2025-05-01T19:07:45.394833Z","shell.execute_reply.started":"2025-05-01T19:07:42.332453Z","shell.execute_reply":"2025-05-01T19:07:45.394114Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nimport os\nimport pandas as pd\n\n# مسیرهای داده‌ها\nBASE_OUTPUT_DIR = \"/kaggle/working/classified_motor_slices_clean\"\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates.xlsx\"  # مسیر برای فایل نهایی Excel\n\n# لیستی برای ذخیره داده‌ها\nall_data = []\n\n# خواندن اطلاعات از هر توموگرام\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        # بارگذاری جدول مختصات توموگرام\n        rotated_df = pd.read_csv(rotated_csv_path)\n        \n        # اضافه کردن نام توموگرام به داده‌ها\n        rotated_df['tomo_id'] = tomo_id\n        \n        # افزودن داده‌ها به لیست\n        all_data.append(rotated_df)\n\n# ترکیب تمامی داده‌ها به یک DataFrame\nfinal_df = pd.concat(all_data, ignore_index=True)\n\n# ذخیره داده‌ها به یک فایل Excel (بدون نیاز به engine خاص)\nfinal_df.to_excel(FINAL_EXCEL_PATH, index=False)\n\nprint(f\"✅ فایل اکسل نهایی با نام {FINAL_EXCEL_PATH} ایجاد شد.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:07:45.395689Z","iopub.execute_input":"2025-05-01T19:07:45.396032Z","iopub.status.idle":"2025-05-01T19:07:47.924355Z","shell.execute_reply.started":"2025-05-01T19:07:45.395997Z","shell.execute_reply":"2025-05-01T19:07:47.923542Z"}},"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-05-01T19:07:47.925198Z","iopub.execute_input":"2025-05-01T19:07:47.925662Z","iopub.status.idle":"2025-05-01T19:07:47.935616Z","shell.execute_reply.started":"2025-05-01T19:07:47.925639Z","shell.execute_reply":"2025-05-01T19:07:47.934351Z"}},"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-05-01T19:07:47.936227Z","iopub.execute_input":"2025-05-01T19:07:47.936417Z","iopub.status.idle":"2025-05-01T19:07:48.668596Z","shell.execute_reply.started":"2025-05-01T19:07:47.936400Z","shell.execute_reply":"2025-05-01T19:07:48.667750Z"}},"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]  # Example: \"tomo_00e047\"\nslice_number = int(third_image.split('_')[2][5:])  # Example: \"slice0169\" -> 169\n\n# Extract the rotation angle (we need to convert \"rot270\" to 270)\nrotation_angle_str = third_image.split('_')[3]  # 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-05-01T19:07:48.671065Z","iopub.execute_input":"2025-05-01T19:07:48.671287Z","iopub.status.idle":"2025-05-01T19:07:49.350451Z","shell.execute_reply.started":"2025-05-01T19:07:48.671267Z","shell.execute_reply":"2025-05-01T19:07:49.349523Z"}},"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[1000]  # 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] # Example: \"tomo_00e047\"\nslice_number = int(third_image.split('_')[2][5:])  # Example: \"slice0169\" -> 169\n\n# Extract rotation angle (we need to convert \"rot270\" to 270)\nrotation_angle_str = third_image.split('_')[3]  # 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-05-01T19:07:49.351992Z","iopub.execute_input":"2025-05-01T19:07:49.352237Z","iopub.status.idle":"2025-05-01T19:07:50.246258Z","shell.execute_reply.started":"2025-05-01T19:07:49.352216Z","shell.execute_reply":"2025-05-01T19:07:50.245301Z"}},"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":{"execution":{"iopub.status.busy":"2025-05-01T19:07:50.246984Z","iopub.execute_input":"2025-05-01T19:07:50.247368Z","iopub.status.idle":"2025-05-01T19:07:50.854057Z","shell.execute_reply.started":"2025-05-01T19:07:50.247322Z","shell.execute_reply":"2025-05-01T19:07:50.853158Z"},"papermill":{"duration":1.472715,"end_time":"2025-04-06T20:42:38.876312","exception":false,"start_time":"2025-04-06T20:42:37.403597","status":"completed"},"tags":[],"trusted":true},"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] \n    slice_number = int(image_name.split('_')[2][5:])\n    \n    # Correctly extract the rotation angle\n    rotation_angle_str = image_name.split('_')[3]\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 50 is used as the object size)\n    real_width = 30  # Actual object width (in pixels)\n    real_height = 30  # 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-05-01T19:07:50.855289Z","iopub.execute_input":"2025-05-01T19:07:50.855545Z","iopub.status.idle":"2025-05-01T19:08:04.549097Z","shell.execute_reply.started":"2025-05-01T19:07:50.855514Z","shell.execute_reply":"2025-05-01T19:08:04.548118Z"}},"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_clean2\"\nTRUST_RANGE = 0  # Only next 5 slices\nos.makedirs(BASE_OUTPUT_DIR, exist_ok=True)\n\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_2.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-05-01T19:08:04.550088Z","iopub.execute_input":"2025-05-01T19:08:04.550314Z","iopub.status.idle":"2025-05-01T19:08:32.264243Z","shell.execute_reply.started":"2025-05-01T19:08:04.550295Z","shell.execute_reply":"2025-05-01T19:08:32.263351Z"}},"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_clean2\"\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-05-01T19:08:32.265122Z","iopub.execute_input":"2025-05-01T19:08:32.265356Z","iopub.status.idle":"2025-05-01T19:08:32.464515Z","shell.execute_reply.started":"2025-05-01T19:08:32.265337Z","shell.execute_reply":"2025-05-01T19:08:32.463522Z"}},"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_clean2\"\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images2\"  # 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-05-01T19:08:32.465676Z","iopub.execute_input":"2025-05-01T19:08:32.465993Z","iopub.status.idle":"2025-05-01T19:08:33.945210Z","shell.execute_reply.started":"2025-05-01T19:08:32.465969Z","shell.execute_reply":"2025-05-01T19:08:33.944486Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"###########\nimport os\nimport pandas as pd\n\n# مسیرهای داده‌ها\nBASE_OUTPUT_DIR = \"/kaggle/working/classified_motor_slices_clean2\"\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates2.xlsx\"  # مسیر برای فایل نهایی Excel\n\n# لیستی برای ذخیره داده‌ها\nall_data = []\n\n# خواندن اطلاعات از هر توموگرام\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_2.csv\")\n    \n    if os.path.exists(rotated_csv_path):\n        # بارگذاری جدول مختصات توموگرام\n        rotated_df = pd.read_csv(rotated_csv_path)\n        \n        # اضافه کردن نام توموگرام به داده‌ها\n        rotated_df['tomo_id'] = tomo_id\n        \n        # افزودن داده‌ها به لیست\n        all_data.append(rotated_df)\n\n# ترکیب تمامی داده‌ها به یک DataFrame\nfinal_df = pd.concat(all_data, ignore_index=True)\n\n# ذخیره داده‌ها به یک فایل Excel (بدون نیاز به engine خاص)\nfinal_df.to_excel(FINAL_EXCEL_PATH, index=False)\n\nprint(f\"✅ فایل اکسل نهایی با نام {FINAL_EXCEL_PATH} ایجاد شد.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:08:33.945963Z","iopub.execute_input":"2025-05-01T19:08:33.946180Z","iopub.status.idle":"2025-05-01T19:08:34.484834Z","shell.execute_reply.started":"2025-05-01T19:08:33.946162Z","shell.execute_reply":"2025-05-01T19:08:34.483893Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#####################\n\n# Path to the final Excel file\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates2.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-05-01T19:08:34.485751Z","iopub.execute_input":"2025-05-01T19:08:34.486067Z","iopub.status.idle":"2025-05-01T19:08:34.632079Z","shell.execute_reply.started":"2025-05-01T19:08:34.486031Z","shell.execute_reply":"2025-05-01T19:08:34.631347Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n######################\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images2\"\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-05-01T19:08:34.632938Z","iopub.execute_input":"2025-05-01T19:08:34.633377Z","iopub.status.idle":"2025-05-01T19:08:34.639464Z","shell.execute_reply.started":"2025-05-01T19:08:34.633353Z","shell.execute_reply":"2025-05-01T19:08:34.638684Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"######################\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images2\"\n\n# Path to the final Excel file\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates2.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-05-01T19:08:34.640371Z","iopub.execute_input":"2025-05-01T19:08:34.640587Z","iopub.status.idle":"2025-05-01T19:08:35.000777Z","shell.execute_reply.started":"2025-05-01T19:08:34.640568Z","shell.execute_reply":"2025-05-01T19:08:34.999913Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n######################\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images2\"\n\n# Path to the final Excel file\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates2.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[1100]  # 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-05-01T19:08:35.001663Z","iopub.execute_input":"2025-05-01T19:08:35.001930Z","iopub.status.idle":"2025-05-01T19:08:35.356033Z","shell.execute_reply.started":"2025-05-01T19:08:35.001909Z","shell.execute_reply":"2025-05-01T19:08:35.355163Z"}},"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_clean2\"\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-05-01T19:08:35.356920Z","iopub.execute_input":"2025-05-01T19:08:35.357207Z","iopub.status.idle":"2025-05-01T19:08:35.698862Z","shell.execute_reply.started":"2025-05-01T19:08:35.357180Z","shell.execute_reply":"2025-05-01T19:08:35.698140Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n##################\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images2\"\n\n# Path to the final Excel file\nFINAL_EXCEL_PATH = \"/kaggle/working/final_motor_coordinates2.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 50 is used as the object size)\n    real_width = 30  # Actual object width (in pixels)\n    real_height =30  # 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-05-01T19:08:35.699691Z","iopub.execute_input":"2025-05-01T19:08:35.699948Z","iopub.status.idle":"2025-05-01T19:08:39.184096Z","shell.execute_reply.started":"2025-05-01T19:08:35.699925Z","shell.execute_reply":"2025-05-01T19:08:39.183214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Path to the final images folder\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images2\"\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')])[142]  # 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":{"papermill":{"duration":0.378339,"end_time":"2025-04-06T20:43:06.311266","exception":false,"start_time":"2025-04-06T20:43:05.932927","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:08:39.185094Z","iopub.execute_input":"2025-05-01T19:08:39.185431Z","iopub.status.idle":"2025-05-01T19:08:39.392012Z","shell.execute_reply.started":"2025-05-01T19:08:39.185407Z","shell.execute_reply":"2025-05-01T19:08:39.391136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nfrom PIL import Image, ImageDraw\nimport matplotlib.pyplot as plt\n\n# مسیر پوشه تصاویر\nFINAL_IMAGES_DIR = \"/kaggle/working/all_motor_images\"\n\n# مسیر پوشه لیبل‌های YOLO\nYOLO_LABELS_DIR = \"/kaggle/working/yolo_labels\"\n\n# بررسی وجود پوشه تصاویر\nif not os.path.exists(FINAL_IMAGES_DIR):\n    os.makedirs(FINAL_IMAGES_DIR, exist_ok=True)\n    print(f\"✅ Directory '{FINAL_IMAGES_DIR}' created successfully.\")\nelse:\n    print(f\"📂 Directory '{FINAL_IMAGES_DIR}' already exists.\")\n\n# بررسی وجود پوشه لیبل‌ها\nif not os.path.exists(YOLO_LABELS_DIR):\n    os.makedirs(YOLO_LABELS_DIR, exist_ok=True)\n    print(f\"✅ Directory '{YOLO_LABELS_DIR}' created successfully.\")\nelse:\n    print(f\"📂 Directory '{YOLO_LABELS_DIR}' already exists.\")\n\n# گرفتن لیست تصاویر\nimage_files = sorted([f for f in os.listdir(FINAL_IMAGES_DIR) if f.endswith('.jpg')])\n\n# بررسی اینکه حداقل 11 تصویر وجود داشته باشد\nif len(image_files) <= 10:\n    raise ValueError(f\"⚠️ Not enough images in '{FINAL_IMAGES_DIR}' to select the 11th image.\")\n\n# انتخاب تصویر یازدهم (index 10)\nimage_name = image_files[10]\nimage_path = os.path.join(FINAL_IMAGES_DIR, image_name)\n\n# باز کردن تصویر\nimg = Image.open(image_path)\n\n# مسیر فایل لیبل مربوط به این تصویر\nlabel_file_path = os.path.join(YOLO_LABELS_DIR, f\"{os.path.splitext(image_name)[0]}.txt\")\n\n# بررسی وجود فایل لیبل\nif not os.path.exists(label_file_path):\n    raise FileNotFoundError(f\"⚠️ Label file '{label_file_path}' not found.\")\n\n# خواندن لیبل‌های YOLO\nwith open(label_file_path, 'r') as f:\n    label_data = f.readlines()\n\n# ترسیم باکس‌های مرزی\ndraw = ImageDraw.Draw(img)\nimage_width, image_height = img.size\n\nfor label in label_data:\n    parts = label.strip().split()\n    if len(parts) != 5:\n        print(f\"⚠️ Skipping invalid label line: {label}\")\n        continue\n\n    class_id, x_center, y_center, width, height = map(float, parts)\n\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    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    # رسم مستطیل\n    draw.rectangle([left, top, right, bottom], outline=\"red\", width=3)\n\n# نمایش تصویر\nplt.imshow(img)\nplt.axis('off')  # حذف محورهای x و y\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:08:39.392992Z","iopub.execute_input":"2025-05-01T19:08:39.393292Z","iopub.status.idle":"2025-05-01T19:08:39.539750Z","shell.execute_reply.started":"2025-05-01T19:08:39.393263Z","shell.execute_reply":"2025-05-01T19:08:39.538979Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport shutil\n\nsource_folder = '/kaggle/working/all_motor_images2'\ndestination_folder = '/kaggle/working/all_motor_images'\nos.makedirs(destination_folder, exist_ok=True)\n\nfor filename in os.listdir(source_folder):\n    if filename.lower().endswith('.jpg'):\n        src_path = os.path.join(source_folder, filename)\n        dst_path = os.path.join(destination_folder, filename)\n\n        shutil.copy2(src_path, dst_path)\n        os.remove(src_path)\n\n        print(f\"{filename} → منتقل و حذف شد.\")\n\nprint(\"✅ عملیات انتقال و حذف فایل‌های jpg با موفقیت انجام شد.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:08:39.540597Z","iopub.execute_input":"2025-05-01T19:08:39.540849Z","iopub.status.idle":"2025-05-01T19:08:41.330006Z","shell.execute_reply.started":"2025-05-01T19:08:39.540802Z","shell.execute_reply":"2025-05-01T19:08:41.329255Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import shutil\nimport os\n\nfolders_to_delete = [\n    \"/kaggle/working/all_motor_images2\"\n]\n\nfor folder_path in folders_to_delete:\n    if os.path.exists(folder_path):\n        shutil.rmtree(folder_path)\n        print(f\"پوشه {folder_path} با موفقیت حذف شد.\")\n    else:\n        print(f\"پوشه {folder_path} پیدا نشد.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:08:41.330866Z","iopub.execute_input":"2025-05-01T19:08:41.331102Z","iopub.status.idle":"2025-05-01T19:08:41.337385Z","shell.execute_reply.started":"2025-05-01T19:08:41.331082Z","shell.execute_reply":"2025-05-01T19:08:41.336458Z"}},"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.2, 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":{"papermill":{"duration":7.986936,"end_time":"2025-04-06T20:43:14.437759","exception":false,"start_time":"2025-04-06T20:43:06.450823","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:08:41.340855Z","iopub.execute_input":"2025-05-01T19:08:41.341081Z","iopub.status.idle":"2025-05-01T19:08:44.517658Z","shell.execute_reply.started":"2025-05-01T19:08:41.341062Z","shell.execute_reply":"2025-05-01T19:08:44.516731Z"}},"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":{"papermill":{"duration":2.127368,"end_time":"2025-04-06T20:43:16.750301","exception":false,"start_time":"2025-04-06T20:43:14.622933","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:08:44.518969Z","iopub.execute_input":"2025-05-01T19:08:44.519278Z","iopub.status.idle":"2025-05-01T19:08:45.759661Z","shell.execute_reply.started":"2025-05-01T19:08:44.519243Z","shell.execute_reply":"2025-05-01T19:08:45.758570Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Path to the yaml file containing dataset information\ndataset_yaml_path = \"/kaggle/working/motor_dataset/dataset.yaml\"","metadata":{"papermill":{"duration":7423.262223,"end_time":"2025-04-06T22:47:00.155957","exception":false,"start_time":"2025-04-06T20:43:16.893734","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:08:45.760718Z","iopub.execute_input":"2025-05-01T19:08:45.761116Z","iopub.status.idle":"2025-05-01T19:08:46.197753Z","shell.execute_reply.started":"2025-05-01T19:08:45.761076Z","shell.execute_reply":"2025-05-01T19:08:46.196627Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from ultralytics import YOLO\n\n# مرحله 1: بارگذاری وزن اولیه و آموزش 50 epoch بدون فریز\nmodel = YOLO(\"/kaggle/input/ultralytics-offlineinstall-yolo12-weights/yolov12-weights/yolo12s.pt\")\n\nmodel.train(\n    data=dataset_yaml_path,\n    epochs=1,\n    imgsz=960,\n    batch=8,\n    lr0=1e-4,\n    lrf=0.1,\n    warmup_epochs=0,\n    val=True,\n    mosaic=1,\n    mixup=0,\n    degrees=10,\n    scale=0.25,\n    name='m1_initial'\n)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:08:46.198714Z","iopub.execute_input":"2025-05-01T19:08:46.199058Z","iopub.status.idle":"2025-05-01T19:24:28.097229Z","shell.execute_reply.started":"2025-05-01T19:08:46.199010Z","shell.execute_reply":"2025-05-01T19:24:28.096192Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# مرحله 2: بارگذاری مدل آموزش‌دیده از مرحله قبل\nmodel = YOLO('runs/detect/m1_initial/weights/last.pt')  # مسیر فایل خروجی مرحله اول\n\n# فریز کردن لایه‌های Backbone (لایه‌های 0 تا 9)\nfor i, layer in enumerate(model.model.model[:10]):\n    for param in layer.parameters():\n        param.requires_grad = False\n\n# ادامه آموزش با فریز کردن - 150 epoch دیگر\nmodel.train(\n    data=dataset_yaml_path,\n    epochs=1,\n    imgsz=960,\n    batch=8,\n    lr0=1e-4,\n    lrf=0.1,\n    warmup_epochs=0,\n    val=True,\n    mosaic=1,\n    mixup=0,\n    degrees=10,\n    scale=0.25,\n    name='m1_frozen'\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:24:28.098330Z","iopub.execute_input":"2025-05-01T19:24:28.098687Z","iopub.status.idle":"2025-05-01T19:41:56.475920Z","shell.execute_reply.started":"2025-05-01T19:24:28.098662Z","shell.execute_reply":"2025-05-01T19:41:56.474787Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n\n# Load the trained model\nmodel = YOLO(\"runs/detect/m1_frozen/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":{"papermill":{"duration":221.524983,"end_time":"2025-04-06T22:50:44.741735","exception":false,"start_time":"2025-04-06T22:47:03.216752","status":"completed"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2025-05-01T19:41:56.477131Z","iopub.execute_input":"2025-05-01T19:41:56.477415Z","execution_failed":"2025-05-01T19:43:48.447Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(results.results_dict)\n","metadata":{"papermill":{"duration":3.108761,"end_time":"2025-04-06T22:50:51.149236","exception":false,"start_time":"2025-04-06T22:50:48.040475","status":"completed"},"tags":[],"trusted":true,"execution":{"execution_failed":"2025-05-01T19:43:48.447Z"}},"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":{"papermill":{"duration":3.923616,"end_time":"2025-04-06T22:50:58.300409","exception":false,"start_time":"2025-04-06T22:50:54.376793","status":"completed"},"tags":[],"trusted":true,"execution":{"execution_failed":"2025-05-01T19:43:48.447Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{"papermill":{"duration":3.126709,"end_time":"2025-04-06T22:51:13.562041","exception":false,"start_time":"2025-04-06T22:51:10.435332","status":"completed"},"tags":[]}},{"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=\"runs/detect/m1_frozen/weights/best.pt\"\n\n# Define detection and processing parameters\nCONFIDENCE_THRESHOLD = 0.45\nMAX_DETECTIONS_PER_TOMO = 6\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":{"papermill":{"duration":3.134999,"end_time":"2025-04-06T22:51:19.960443","exception":false,"start_time":"2025-04-06T22:51:16.825444","status":"completed"},"tags":[],"trusted":true,"execution":{"execution_failed":"2025-05-01T19:43:48.448Z"}},"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":{"papermill":{"duration":3.239778,"end_time":"2025-04-06T22:51:26.324674","exception":false,"start_time":"2025-04-06T22:51:23.084896","status":"completed"},"tags":[],"trusted":true,"execution":{"execution_failed":"2025-05-01T19:43:48.448Z"}},"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":{"papermill":{"duration":3.192372,"end_time":"2025-04-06T22:51:32.748432","exception":false,"start_time":"2025-04-06T22:51:29.556060","status":"completed"},"tags":[],"trusted":true,"execution":{"execution_failed":"2025-05-01T19:43:48.448Z"}},"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":{"papermill":{"duration":53.154605,"end_time":"2025-04-06T22:52:29.203619","exception":false,"start_time":"2025-04-06T22:51:36.049014","status":"completed"},"tags":[],"trusted":true,"execution":{"execution_failed":"2025-05-01T19:43:48.448Z"}},"outputs":[],"execution_count":null}]}