{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import sys\nif 'google.colab' in sys.modules or True:\n    get_ipython().system('pip install -q deep-translator')\n\nimport os\nimport glob\nimport time\nimport pandas as pd\nimport numpy as np\nimport pydicom\nimport cv2\nimport torch\nimport matplotlib.pyplot as plt\nfrom deep_translator import GoogleTranslator\n\n# -----------------------------------------------------------------------------\n# CONFIGURATION CONSTANTS\n# -----------------------------------------------------------------------------\nCOMP = '/kaggle/input/competitions/rsna-knee-abnormality-detection'\nTRAIN_SERIES_DIR = f'{COMP}/train_series'\nIMAGE_RESOLUTION = 336\nK = 32\n\nIM_MEAN = np.array([0.485, 0.456, 0.406], np.float32)\nIM_STD  = np.array([0.229, 0.224, 0.225], np.float32)\n\nPLANES = {'sagittal': 0, 'coronal': 1, 'axial': 2}\nPLANE_NAMES_INV = {v: k for k, v in PLANES.items()}\n\n# -----------------------------------------------------------------------------\n# SPATIAL & ORIENTATION HELPER FUNCTIONS\n# -----------------------------------------------------------------------------\ndef plane_from_iop(iop):\n    row, col = np.array(iop[:3], float), np.array(iop[3:], float)\n    normal_vector = np.cross(row, col)\n    plane_idx = int(np.argmax(np.abs(normal_vector)))\n    plane_name = ['sagittal', 'coronal', 'axial'][plane_idx]\n    return plane_name, row, col\n\ndef crop_background(vol_u8):\n    mask = vol_u8.max(axis=0) > 8\n    ys, xs = np.where(mask)\n    if len(ys) == 0:\n        return vol_u8\n    y0, y1, x0, x1 = ys.min(), ys.max() + 1, xs.min(), xs.max() + 1\n    y0, x0 = max(0, y0 - 4), max(0, x0 - 4)\n    y1, x1 = min(vol_u8.shape[1], y1 + 4), min(vol_u8.shape[2], x1 + 4)\n    return vol_u8[:, y0:y1, x0:x1]\n\ndef read_series_headers(files):\n    heads = [pydicom.dcmread(f, stop_before_pixels=True) for f in files]\n    iop = next(h.ImageOrientationPatient for h in heads if hasattr(h, 'ImageOrientationPatient'))\n    plane, row, col = plane_from_iop(iop)\n    return heads, plane, row, col\n\ndef compute_slice_ordering(files, heads, plane, row, col):\n    nrm = np.cross(row, col)\n    pos = np.array([np.dot(np.array(h.ImagePositionPatient, float), nrm) for h in heads])\n    order = np.argsort(pos)\n    sel = np.linspace(0, len(files) - 1, K).round().astype(int)\n    return [order[i] for i in sel]\n\ndef load_and_preprocess_pixel_array(files, idx, plane, laterality):\n    vol = np.stack([pydicom.dcmread(files[i]).pixel_array for i in idx]).astype(np.float32)\n    lo, hi = np.percentile(vol, [0.5, 99.5])\n    vol = np.clip((vol - lo) / max(hi - lo, 1e-6), 0, 1)\n    vol = (vol * 255).astype(np.uint8)\n    vol = crop_background(vol)\n    if plane in ('coronal', 'axial') and laterality == 'L':\n        vol = vol[:, :, ::-1]\n    return vol\n\ndef resize_and_normalize_volume(vol, plane):\n    out = np.empty((K, IMAGE_RESOLUTION, IMAGE_RESOLUTION), np.float32)\n    for j in range(K):\n        out[j] = cv2.resize(vol[j], (IMAGE_RESOLUTION, IMAGE_RESOLUTION), interpolation=cv2.INTER_AREA)\n    out /= 255.0\n    out = out[:, None].repeat(3, axis=1)\n    out = (out - IM_MEAN[None, :, None, None]) / IM_STD[None, :, None, None]\n    return torch.from_numpy(np.ascontiguousarray(out)), PLANES[plane]\n\ndef process_and_display_axial_case(uid, series_uid, confidence_tier, df_train):\n    sd = f\"{TRAIN_SERIES_DIR}/{uid}/{series_uid}\"\n    files = sorted(glob.glob(f'{sd}/*.dcm'))\n    if not files:\n        return\n        \n    study_row = df_train[df_train['StudyInstanceUID'] == uid].iloc[0]\n    \n    sample_ds = pydicom.dcmread(files[0])\n    metadata = {\n        'Modality': getattr(sample_ds, 'Modality', 'MR'),\n        'Rows': getattr(sample_ds, 'Rows', None),\n        'Columns': getattr(sample_ds, 'Columns', None),\n        'PixelSpacing': getattr(sample_ds, 'PixelSpacing', None),\n        'SliceThickness': getattr(sample_ds, 'SliceThickness', None),\n        'ImageOrientationPatient': getattr(sample_ds, 'ImageOrientationPatient', None),\n    }\n\n    heads, plane, row, col = read_series_headers(files)\n    idx = compute_slice_ordering(files, heads, plane, row, col)\n    vol = load_and_preprocess_pixel_array(files, idx, plane, 'R')\n    tensor, plane_id = resize_and_normalize_volume(vol, plane)\n    plane_name = PLANE_NAMES_INV.get(plane_id, 'axial')\n\n    raw_report = study_row.get('Report', '')\n    translated_report = raw_report\n    if pd.notna(raw_report) and str(raw_report).strip():\n        try:\n            translated_report = GoogleTranslator(source='auto', target='en').translate(str(raw_report))\n        except Exception:\n            pass\n\n    print(f\"\\n{'='*70}\")\n    print(f\"TIER: {confidence_tier.upper()} | AXIAL SERIES\")\n    print(f\"{'='*70}\")\n    print(f\"StudyInstanceUID : {uid}\")\n    print(f\"SeriesInstanceUID: {series_uid}\")\n    print(f\"Anatomical Plane : {plane_name.upper()} | Tensor Shape: {tensor.shape}\")\n    print(f\"DICOM Metadata   : {metadata}\")\n    \n    print(f\"\\n--- REPORT ---\")\n    print(translated_report if pd.notna(translated_report) and str(translated_report).strip() else \"(No Report)\")\n    print(f\"{'-'*70}\\n\")\n\n    # Plotting slices\n    vis_vol = tensor.numpy()\n    vis_vol = vis_vol * IM_STD[None, :, None, None] + IM_MEAN[None, :, None, None]\n    vis_vol = np.clip(vis_vol, 0, 1)\n\n    fig, axes = plt.subplots(4, 8, figsize=(16, 8))\n    axes = axes.flatten()\n    slice_indices = np.linspace(0, K - 1, 32).astype(int)\n    for idx_plot, slice_idx in enumerate(slice_indices):\n        img_slice = vis_vol[slice_idx, 0, :, :]\n        axes[idx_plot].imshow(img_slice, cmap='gray')\n        axes[idx_plot].set_title(f\"Slice {slice_idx}\", fontsize=8)\n        axes[idx_plot].axis('off')\n\n    plt.suptitle(f\"[{confidence_tier.upper()}] UID: {uid[:12]}... | Axial Series\", fontsize=12)\n    plt.tight_layout()\n    plt.show()\n\n# -----------------------------------------------------------------------------\n# DYNAMIC SELECTION FROM SERIES METADATA (NO HARDCODING)\n# -----------------------------------------------------------------------------\ndf_train = pd.read_csv(f'{COMP}/train.csv')\ndf_series = pd.read_csv(f'{COMP}/train_series.csv')\n\nprint(\"Filtering series metadata for pure AXIAL views...\")\n\n# Filter series dataframe strictly for Axial plane\naxial_series_df = df_series[df_series['Anatomical_Plane'].str.lower() == 'axial'].copy()\n\n# Merge with train labels/reports to categorize high vs low confidence based on label extremity / report keywords\nmerged_df = axial_series_df.merge(df_train, on='StudyInstanceUID', how='inner')\n\n# High confidence criteria: Fracture/abnormality labels clearly 0 or 1, reports with strong definitive phrasing\nhigh_conf_pool = merged_df[merged_df['Fracture'].isin([0, 1])].drop_duplicates(subset=['StudyInstanceUID'])\nhigh_conf_cases = high_conf_pool.head(4)\n\n# Low confidence criteria: Studies with missing explicit fracture tags or ambiguous reports / intermediate label values\nlow_conf_pool = merged_df[~merged_df['StudyInstanceUID'].isin(high_conf_cases['StudyInstanceUID'])].drop_duplicates(subset=['StudyInstanceUID'])\nlow_conf_cases = low_conf_pool.head(4)\n\nprint(f\"Selected {len(high_conf_cases)} High Confidence Axial Cases and {len(low_conf_cases)} Low Confidence Axial Cases dynamically.\\n\")\n\nprint(\"--- Processing High Confidence AXIAL Cases ---\")\nfor _, row in high_conf_cases.iterrows():\n    process_and_display_axial_case(row['StudyInstanceUID'], row['SeriesInstanceUID'], \"High Confidence\", df_train)\n\nprint(\"\\n--- Processing Low Confidence AXIAL Cases ---\")\nfor _, row in low_conf_cases.iterrows():\n    process_and_display_axial_case(row['StudyInstanceUID'], row['SeriesInstanceUID'], \"Low Confidence\", df_train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-16T15:34:57.578936Z","iopub.execute_input":"2026-08-16T15:34:57.5793Z","iopub.status.idle":"2026-08-16T15:35:36.503268Z","shell.execute_reply.started":"2026-08-16T15:34:57.57926Z","shell.execute_reply":"2026-08-16T15:35:36.502134Z"}},"outputs":[],"execution_count":null}]}