{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":19989,"databundleVersionId":1160143,"sourceType":"competition"},{"sourceId":11805316,"sourceType":"datasetVersion","datasetId":7413732},{"sourceId":11808378,"sourceType":"datasetVersion","datasetId":7413522}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n# Example of listing files (can be removed if not needed for final notebook)\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Install Ultralytics and its dependency thop from wheels\n!pip install /kaggle/input/ultralytics-whl-pkg/ultralytics_thop-2.0.14-py3-none-any.whl -q --no-deps\n!pip install /kaggle/input/ultralytics-whl-pkg/ultralytics-8.3.133-py3-none-any.whl -q --no-deps\n\nimport glob\nfrom ultralytics import YOLO\nimport matplotlib.pyplot as plt # For displaying images\nimport cv2 # For explicit image loading and resizing","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-14T13:32:21.058189Z","iopub.execute_input":"2025-05-14T13:32:21.058963Z","iopub.status.idle":"2025-05-14T13:32:23.976216Z","shell.execute_reply.started":"2025-05-14T13:32:21.058927Z","shell.execute_reply":"2025-05-14T13:32:23.975300Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- Configuration for Kaggle Environment ---\nKAGGLE_INPUT_DIR = '/kaggle/input/global-wheat-detection'\nKAGGLE_TEST_IMAGE_DIR = os.path.join(KAGGLE_INPUT_DIR, 'test')\nKAGGLE_MODEL_PATH = '/kaggle/input/gwd-yolov8s-best-weights/best.pt' # User provided path\n\nKAGGLE_WORKING_DIR = '/kaggle/working'\nKAGGLE_PREDICT_RUN_DIR = os.path.join(KAGGLE_WORKING_DIR, 'runs_predict') # Temp dir for predict outputs\nKAGGLE_PREDICT_LABEL_DIR = os.path.join(KAGGLE_PREDICT_RUN_DIR, 'predict/labels') # YOLO saves labels here by default\nKAGGLE_OUTPUT_SUBMISSION_FILE = os.path.join(KAGGLE_WORKING_DIR, 'submission.csv')\n\nIMAGE_WIDTH = 1024.0\nIMAGE_HEIGHT = 1024.0\nCONFIDENCE_THRESHOLD = 0.35\n\nprint(f\"Kaggle Test Image Dir: {KAGGLE_TEST_IMAGE_DIR}\")\nprint(f\"Kaggle Model Path: {KAGGLE_MODEL_PATH}\")\nprint(f\"Kaggle Submission File Path: {KAGGLE_OUTPUT_SUBMISSION_FILE}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-14T13:32:23.977817Z","iopub.execute_input":"2025-05-14T13:32:23.978124Z","iopub.status.idle":"2025-05-14T13:32:23.984441Z","shell.execute_reply.started":"2025-05-14T13:32:23.978093Z","shell.execute_reply":"2025-05-14T13:32:23.983765Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- 1. Perform Prediction ---\nprint(\"\\nLoading YOLOv8 model...\")\nmodel = YOLO(KAGGLE_MODEL_PATH)\nprint(\"Model loaded.\")\n\nprint(f\"\\nStarting prediction on images in: {KAGGLE_TEST_IMAGE_DIR}\")\n# os.makedirs(KAGGLE_PREDICT_RUN_DIR, exist_ok=True) # Not strictly needed if not saving txt files via project/name\n# KAGGLE_PREDICT_LABEL_DIR is also not used if we process results directly\n\n# The main 'results' variable will not be a list of all results at once anymore.\n# We will process image by image. Visualization will be handled differently if re-added.\nprint(\"Predictions will be processed image by image with explicit resizing.\")\n\n# Visualization section removed for now to focus on core submission logic with resize fix.\n# If this works, visualization can be re-added by collecting plotted images from per-image predictions.","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-14T13:32:23.985286Z","iopub.execute_input":"2025-05-14T13:32:23.985588Z","iopub.status.idle":"2025-05-14T13:32:24.075726Z","shell.execute_reply.started":"2025-05-14T13:32:23.985565Z","shell.execute_reply":"2025-05-14T13:32:24.075061Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- 2. Format Predictions for Submission ---\n# This section will now include the prediction loop.\nprint(\"\\nStarting prediction and formatting for submission...\")\n\ndef yolo_to_voc_abs_kaggle(center_x_norm, center_y_norm, width_norm, height_norm, img_w, img_h):\n    w_abs = width_norm * img_w\n    h_abs = height_norm * img_h\n    x_min_abs = (center_x_norm * img_w) - (w_abs / 2.0)\n    y_min_abs = (center_y_norm * img_h) - (h_abs / 2.0)\n    \n    # Ensure results are integers and clamped to image boundaries\n    x_min_abs = max(0, int(round(x_min_abs)))\n    y_min_abs = max(0, int(round(y_min_abs)))\n    w_abs = max(1, int(round(w_abs))) # Ensure width is at least 1\n    h_abs = max(1, int(round(h_abs))) # Ensure height is at least 1\n\n    # Clip to image boundaries\n    if x_min_abs + w_abs > img_w:\n        w_abs = int(img_w - x_min_abs)\n    if y_min_abs + h_abs > img_h:\n        h_abs = int(img_h - y_min_abs)\n    \n    # Ensure width and height are still positive after clipping\n    w_abs = max(1, w_abs)\n    h_abs = max(1, h_abs)\n            \n    return x_min_abs, y_min_abs, w_abs, h_abs\n\n# Initialize submission_data here, before the try block\nsubmission_data = []\n\n# It's safer to iterate over images found in the test directory by the notebook.\ntest_image_files = []\nif os.path.exists(KAGGLE_TEST_IMAGE_DIR):\n    test_image_files = [f for f in os.listdir(KAGGLE_TEST_IMAGE_DIR) if f.lower().endswith(('.png', '.jpg', '.jpeg'))]\nelse:\n    print(f\"ERROR: Test image directory not found at {KAGGLE_TEST_IMAGE_DIR}\")\n\nprint(f\"Found {len(test_image_files)} images in {KAGGLE_TEST_IMAGE_DIR}\")\n\ntry:\n    for image_filename in test_image_files:\n        image_id = os.path.splitext(image_filename)[0]\n        img_path = os.path.join(KAGGLE_TEST_IMAGE_DIR, image_filename)\n        prediction_string_parts = []\n        # print(f\"Processing image: {image_id}\") # Optional: for verbose logging\n\n        try:\n            img = cv2.imread(img_path)\n            if img is None:\n                print(f\"Warning: Failed to load image {img_path}. Appending empty prediction.\")\n                submission_data.append({'image_id': image_id, 'PredictionString': \"\"})\n                continue\n\n            # Explicitly resize to 1024x1024 as per Kaggle discussion advice\n            img_resized = cv2.resize(img, (int(IMAGE_WIDTH), int(IMAGE_HEIGHT)))\n            \n            # Predict on the single resized image\n            # Set verbose=False to reduce log spam per image if many test images\n            individual_results = model.predict(source=img_resized, imgsz=int(IMAGE_WIDTH), conf=CONFIDENCE_THRESHOLD, verbose=False) \n            \n            if individual_results and individual_results[0].boxes and len(individual_results[0].boxes.xywhn) > 0:\n                # .boxes.data gives [x1, y1, x2, y2, conf, cls]\n                # We need normalized [cx, cy, w, h] and conf\n                # results[0].boxes.xywhn gives normalized [cx, cy, w, h]\n                # results[0].boxes.conf gives confidences\n                \n                norm_boxes = individual_results[0].boxes.xywhn.cpu().numpy()\n                confs = individual_results[0].boxes.conf.cpu().numpy()\n\n                for i in range(len(norm_boxes)):\n                    center_x_norm, center_y_norm, width_norm, height_norm = norm_boxes[i]\n                    confidence = confs[i]\n                    \n                    x_abs, y_abs, w_abs, h_abs = yolo_to_voc_abs_kaggle(\n                        center_x_norm, center_y_norm, width_norm, height_norm,\n                        IMAGE_WIDTH, IMAGE_HEIGHT # De-normalize based on the size fed to model (1024x1024)\n                    )\n                    prediction_string_parts.append(f\"{confidence:.4f} {x_abs} {y_abs} {w_abs} {h_abs}\")\n            # else:\n                # print(f\"No boxes found for {image_id} above threshold {CONFIDENCE_THRESHOLD}\")\n                                \n        except Exception as e_img_process:\n            print(f\"Error processing image {image_filename}: {e_img_process}\")\n            # Fallback to empty prediction string for this image if an error occurs during its processing\n        \n        full_prediction_string = \" \".join(prediction_string_parts)\n        submission_data.append({'image_id': image_id, 'PredictionString': full_prediction_string})\n\nexcept Exception as e: # This outer try-except catches errors in the loop itself or file listing\n    print(f\"An error occurred during prediction processing: {e}\")\n    print(\"Will attempt to create a submission file based on sample_submission.csv or headers only.\")\n    # Ensure submission_data is empty if an error occurred mid-processing,\n    # so it falls through to the logic that uses sample_submission.csv\n    submission_data = []\n\n# The yolo_to_voc_abs_kaggle function definition has been moved up.\n\n# The loop for populating submission_data is now inside the try-except block above.\n# This 'if' condition below handles the case where the loop completed but found no predictions for any existing test files.\nif not submission_data and test_image_files: # Check if submission_data is empty AND test_image_files is NOT empty\n     print(\"Warning: No predictions were made or processed, but test images exist. Submission file will have empty PredictionStrings.\")\n     # Ensure all test images are in the submission file, even if with empty predictions\n     for image_filename in test_image_files:\n        image_id = os.path.splitext(image_filename)[0]\n        # Check if already added (should not happen if this block is reached)\n        if not any(d['image_id'] == image_id for d in submission_data):\n            submission_data.append({'image_id': image_id, 'PredictionString': \"\"})\n\n\nsubmission_df = pd.DataFrame(submission_data)\n\n# Ensure the DataFrame is not empty before saving, especially if test_image_files was empty\nif not submission_df.empty:\n    submission_df.to_csv(KAGGLE_OUTPUT_SUBMISSION_FILE, index=False)\n    print(f\"\\nSubmission file created: {KAGGLE_OUTPUT_SUBMISSION_FILE}\")\n    print(f\"Total images in submission: {len(submission_df)}\")\n    print(\"\\nSample of submission (first 5 rows):\")\n    print(submission_df.head())\nelse:\n    # Create an empty submission file with header if no test images were found at all\n    # or if submission_data remained empty for other reasons.\n    # Kaggle might expect a file even if it's just the header.\n    print(\"Warning: No data to write to submission file. Creating a file with header only or based on sample_submission if available.\")\n    sample_submission_path = os.path.join(KAGGLE_INPUT_DIR, 'sample_submission.csv')\n    if os.path.exists(sample_submission_path):\n        try:\n            sample_df = pd.read_csv(sample_submission_path)\n            # Create an empty prediction string for all image_ids in sample_submission\n            # This ensures the submission has the right image_ids if our processing failed to find any.\n            empty_predictions = []\n            for img_id in sample_df['image_id']:\n                empty_predictions.append({'image_id': img_id, 'PredictionString': \"\"})\n            final_empty_df = pd.DataFrame(empty_predictions)\n            final_empty_df.to_csv(KAGGLE_OUTPUT_SUBMISSION_FILE, index=False)\n            print(f\"Created submission file from sample_submission.csv with empty predictions: {KAGGLE_OUTPUT_SUBMISSION_FILE}\")\n        except Exception as e:\n            print(f\"Error reading sample_submission.csv: {e}. Creating a header-only submission file.\")\n            pd.DataFrame(columns=['image_id', 'PredictionString']).to_csv(KAGGLE_OUTPUT_SUBMISSION_FILE, index=False)\n            print(f\"Created header-only submission file: {KAGGLE_OUTPUT_SUBMISSION_FILE}\")\n    else:\n        pd.DataFrame(columns=['image_id', 'PredictionString']).to_csv(KAGGLE_OUTPUT_SUBMISSION_FILE, index=False)\n        print(f\"Created header-only submission file: {KAGGLE_OUTPUT_SUBMISSION_FILE}\")\n\n\nprint(\"\\nNotebook execution finished.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-14T13:32:24.077094Z","iopub.execute_input":"2025-05-14T13:32:24.077364Z","iopub.status.idle":"2025-05-14T13:32:24.553569Z","shell.execute_reply.started":"2025-05-14T13:32:24.077348Z","shell.execute_reply":"2025-05-14T13:32:24.552907Z"}},"outputs":[],"execution_count":null}]}