{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":51753,"databundleVersionId":5692552,"sourceType":"competition"}],"dockerImageVersionId":30458,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# How to make a submission using the Run-Length Encoding format","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom pathlib import Path\nfrom skimage import measure\nfrom skimage.morphology import opening, disk\n\n# --- Configuration & Setup ---\ntry:\n    data_path = Path('/kaggle/input/google-research-identify-contrails-reduce-global-warming')\n    test_recs = os.listdir(data_path / 'test')\n    print(f\"Dataset found at {data_path}. Found {len(test_recs)} test records.\")\nexcept FileNotFoundError:\n    print(\"Kaggle dataset not found. Creating dummy data for demonstration.\")\n    data_path = Path('./dummy_data')\n    test_recs = ['1002653297254493116', '1000834164244036115']\n    for rec in test_recs:\n        os.makedirs(data_path / 'test' / rec, exist_ok=True)\n        np.save(data_path / 'test' / rec / 'band_08.npy', np.random.rand(256, 266, 4) * 100)\n        np.save(data_path / 'test' / rec / 'band_14.npy', np.random.rand(256, 266, 4) * 100)\n        np.save(data_path / 'test' / rec / 'band_15.npy', np.random.rand(256, 266, 4) * 100)\n    dummy_df = pd.DataFrame({'record_id': test_recs, 'encoded_pixels': ''})\n    dummy_df.to_csv(data_path / 'sample_submission.csv', index=False)\n    print(\"Dummy data created successfully.\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2025-08-28T06:42:32.248235Z","iopub.execute_input":"2025-08-28T06:42:32.24849Z","iopub.status.idle":"2025-08-28T06:42:33.924163Z","shell.execute_reply.started":"2025-08-28T06:42:32.248457Z","shell.execute_reply":"2025-08-28T06:42:33.922991Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def rle_encode(x, fg_val=1):\n    dots = np.where(x.T.flatten() == fg_val)[0]\n    run_lengths = []\n    prev = -2\n    for b in dots:\n        if b > prev + 1:\n            run_lengths.extend((b + 1, 0))\n        run_lengths[-1] += 1\n        prev = b\n    return run_lengths\n\ndef list_to_string(x):\n    if x:\n        s = str(x).replace(\"[\", \"\").replace(\"]\", \"\").replace(\",\", \"\")\n    else:\n        s = '-'\n    return s","metadata":{"execution":{"iopub.status.busy":"2025-08-28T06:43:04.781958Z","iopub.execute_input":"2025-08-28T06:43:04.782752Z","iopub.status.idle":"2025-08-28T06:43:04.789045Z","shell.execute_reply.started":"2025-08-28T06:43:04.782704Z","shell.execute_reply":"2025-08-28T06:43:04.788039Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def normalize_band(band):\n    min_val = np.min(band)\n    max_val = np.max(band)\n    if max_val == min_val:\n        return band - min_val\n    return (band - min_val) / (max_val - min_val)\n\ndef create_false_color_composite(rec_path):\n    band_15 = np.load(rec_path / 'band_15.npy').sum(axis=2)\n    band_14 = np.load(rec_path / 'band_14.npy').sum(axis=2)\n    band_08 = np.load(rec_path / 'band_08.npy').sum(axis=2)\n    r = normalize_band(band_15)\n    g = normalize_band(band_14)\n    b = normalize_band(band_08)\n    return np.stack([r, g, b], axis=2)","metadata":{"execution":{"iopub.status.busy":"2025-08-28T06:43:26.429221Z","iopub.execute_input":"2025-08-28T06:43:26.430323Z","iopub.status.idle":"2025-08-28T06:43:26.43686Z","shell.execute_reply.started":"2025-08-28T06:43:26.430279Z","shell.execute_reply":"2025-08-28T06:43:26.435803Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def generate_contrail_mask(rec_path, min_area=20, eccentricity_threshold=0.90, debug=False):\n    \"\"\"\n    Generates a contrail mask.\n    \n    **FIXED**: Relaxed min_area and eccentricity_threshold to be more inclusive.\n    \"\"\"\n    band_14 = np.load(rec_path / 'band_14.npy').sum(axis=2)\n    band_15 = np.load(rec_path / 'band_15.npy').sum(axis=2)\n    feature_map = band_14 - band_15\n\n    threshold = np.percentile(feature_map, 99.5)\n    initial_mask = feature_map > threshold\n\n    cleaned_mask = opening(initial_mask, disk(1))\n    labels = measure.label(cleaned_mask)\n    regions = measure.regionprops(labels)\n\n    final_mask = np.zeros_like(cleaned_mask)\n    \n    if debug:\n        print(f\"  - Adaptive threshold value: {threshold:.4f}\")\n        print(f\"  - Pixels in initial mask: {np.sum(initial_mask)}\")\n        print(f\"  - Found {len(regions)} potential regions before filtering.\")\n\n    kept_regions = 0\n    for region in regions:\n        if region.area > min_area and region.eccentricity > eccentricity_threshold:\n            for coord in region.coords:\n                final_mask[coord[0], coord[1]] = 1\n            kept_regions += 1\n        elif debug:\n            # This part helps you see why regions were discarded\n            # print(f\"    - Discarded Region: Area={region.area}, Eccentricity={region.eccentricity:.2f}\")\n            pass\n\n    if debug:\n        print(f\"  - Kept {kept_regions} regions after filtering.\")\n        print(f\"  - Total pixels in final mask: {np.sum(final_mask)}\")\n\n    return final_mask, feature_map, initial_mask","metadata":{"execution":{"iopub.status.busy":"2025-08-28T06:43:52.514291Z","iopub.execute_input":"2025-08-28T06:43:52.515306Z","iopub.status.idle":"2025-08-28T06:43:52.523348Z","shell.execute_reply.started":"2025-08-28T06:43:52.515268Z","shell.execute_reply":"2025-08-28T06:43:52.522527Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def visualize_detection_process(record_id, false_color_img, feature_map, initial_mask, final_mask):\n    fig, axes = plt.subplots(2, 2, figsize=(15, 12))\n    fig.suptitle(f'Advanced Contrail Detection Analysis: Record {record_id}', fontsize=20, y=0.95)\n    plt.style.use('dark_background')\n    \n    axes[0, 0].imshow(false_color_img); axes[0, 0].set_title('A: False-Color Composite', fontsize=14); axes[0, 0].axis('off')\n    im = axes[0, 1].imshow(feature_map, cmap='magma'); axes[0, 1].set_title('B: Enhanced Feature Map (Band 14 - 15)', fontsize=14); axes[0, 1].axis('off')\n    fig.colorbar(im, ax=axes[0, 1], orientation='vertical', fraction=0.046, pad=0.04)\n    axes[1, 0].imshow(initial_mask, cmap='gray'); axes[1, 0].set_title('C: Initial Mask (Adaptive Threshold)', fontsize=14); axes[1, 0].axis('off')\n    axes[1, 1].imshow(false_color_img)\n    final_mask_overlay = np.ma.masked_where(final_mask == 0, final_mask)\n    axes[1, 1].imshow(final_mask_overlay, cmap='cool', alpha=0.7, interpolation='none')\n    axes[1, 1].set_title('D: Final Mask (Filtered by Shape)', fontsize=14); axes[1, 1].axis('off')\n    \n    plt.tight_layout(rect=[0, 0, 1, 0.92])\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2025-08-28T06:44:11.9447Z","iopub.execute_input":"2025-08-28T06:44:11.945066Z","iopub.status.idle":"2025-08-28T06:44:11.954334Z","shell.execute_reply.started":"2025-08-28T06:44:11.945035Z","shell.execute_reply":"2025-08-28T06:44:11.953325Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def main():\n    submission_path = data_path / 'sample_submission.csv'\n    submission = pd.read_csv(submission_path, index_col='record_id')\n\n    # To process all, use: for rec in test_recs:\n    for i, rec in enumerate(test_recs[:2]): # Process first 2 records for demo\n        print(f\"Processing record: {rec}...\")\n        rec_path = data_path / 'test' / rec\n\n        # Generate mask with debugging info enabled\n        final_mask, feature_map, initial_mask = generate_contrail_mask(rec_path, debug=True)\n        \n        # Only visualize if a contrail was detected to avoid blank plots\n        if np.sum(final_mask) > 0:\n            false_color_img = create_false_color_composite(rec_path)\n            visualize_detection_process(rec, false_color_img, feature_map, initial_mask, final_mask)\n        else:\n            print(f\"  - No contrails detected for record {rec} after filtering.\")\n\n        encoded_pixels = list_to_string(rle_encode(final_mask))\n        submission.loc[int(rec), 'encoded_pixels'] = encoded_pixels\n        print(\"-\" * 30)\n\n    submission.to_csv('submission.csv')\n    print(\"\\nSubmission file 'submission_advanced_v2.csv' created successfully.\")\n    print(\"Final submission head:\")\n    print(submission.head())\n\nif __name__ == '__main__':\n    main()","metadata":{"execution":{"iopub.status.busy":"2025-08-28T06:46:17.416414Z","iopub.execute_input":"2025-08-28T06:46:17.416914Z","iopub.status.idle":"2025-08-28T06:46:19.341133Z","shell.execute_reply.started":"2025-08-28T06:46:17.416878Z","shell.execute_reply":"2025-08-28T06:46:19.339937Z"},"trusted":true},"outputs":[],"execution_count":null}]}