{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":4104,"databundleVersionId":46661,"sourceType":"competition"},{"sourceId":7866129,"sourceType":"datasetVersion","datasetId":4614938},{"sourceId":7869237,"sourceType":"datasetVersion","datasetId":4617269}],"dockerImageVersionId":30673,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nfrom glob import glob\nimport os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport seaborn as sns\nimport cv2","metadata":{"execution":{"iopub.status.busy":"2026-08-02T10:41:18.262323Z","iopub.execute_input":"2026-08-02T10:41:18.263162Z","iopub.status.idle":"2026-08-02T10:41:22.323667Z","shell.execute_reply.started":"2026-08-02T10:41:18.263124Z","shell.execute_reply":"2026-08-02T10:41:22.321713Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"file_lbl=\"/kaggle/input/diabetic-retinopathy-detection/trainLabels.csv.zip\"\ndf_train=pd.read_csv(file_lbl,sep=',')\ndf_train","metadata":{"execution":{"iopub.status.busy":"2026-08-02T10:41:26.132039Z","iopub.execute_input":"2026-08-02T10:41:26.132683Z","iopub.status.idle":"2026-08-02T10:41:26.222740Z","shell.execute_reply.started":"2026-08-02T10:41:26.132636Z","shell.execute_reply":"2026-08-02T10:41:26.220895Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Structure\n\nprint(df_train.head())\nprint(df_train.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-02T06:11:40.856775Z","iopub.execute_input":"2026-08-02T06:11:40.857798Z","iopub.status.idle":"2026-08-02T06:11:40.865071Z","shell.execute_reply.started":"2026-08-02T06:11:40.857735Z","shell.execute_reply":"2026-08-02T06:11:40.863660Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Distribution des labels\nplt.figure(figsize=(8,4))\nsns.countplot(x=df_train['level'], palette='viridis')\nplt.title(\"Distribution des niveaux de rétinopathie\")\nplt.show()\n\ndf_train['level'].value_counts().sort_index()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-02T06:11:45.245682Z","iopub.execute_input":"2026-08-02T06:11:45.246104Z","iopub.status.idle":"2026-08-02T06:11:45.562623Z","shell.execute_reply.started":"2026-08-02T06:11:45.246070Z","shell.execute_reply":"2026-08-02T06:11:45.561569Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Histogramme\ndf_train['level'].hist(bins=5, figsize=(6,4))\nplt.title(\"Histogramme des labels\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-02T06:11:52.983452Z","iopub.execute_input":"2026-08-02T06:11:52.983851Z","iopub.status.idle":"2026-08-02T06:11:53.239732Z","shell.execute_reply.started":"2026-08-02T06:11:52.983820Z","shell.execute_reply":"2026-08-02T06:11:53.238647Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Vérifions l'ordinalité\ndf_train['level'].plot(kind='hist', bins=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-02T06:11:57.009746Z","iopub.execute_input":"2026-08-02T06:11:57.010112Z","iopub.status.idle":"2026-08-02T06:11:57.257154Z","shell.execute_reply.started":"2026-08-02T06:11:57.010087Z","shell.execute_reply":"2026-08-02T06:11:57.255973Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Inspectons quelques images par classe\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\nTRAIN_DIR = \"/kaggle/input/diabetic-retinopathy-train-unzipped/train\"\n\ndef show_samples(level, n=5):\n    samples = df_train[df_train['level'] == level].sample(n)\n    fig, ax = plt.subplots(1, n, figsize=(15,5))\n    for i, row in enumerate(samples.itertuples()):\n        img_path = f\"{TRAIN_DIR}/{row.image}.jpeg\"\n        img = Image.open(img_path).convert(\"RGB\")\n        ax[i].imshow(img)\n        ax[i].set_title(f\"{row.image} (level {level})\")\n        ax[i].axis('off')\n\nfor lvl in range(5):\n    print(f\"\\n=== Level {lvl} ===\")\n    show_samples(lvl, n=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-02T06:12:48.893729Z","iopub.execute_input":"2026-08-02T06:12:48.894212Z","iopub.status.idle":"2026-08-02T06:13:22.517009Z","shell.execute_reply.started":"2026-08-02T06:12:48.894178Z","shell.execute_reply":"2026-08-02T06:13:22.515836Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Résolution d'image\n\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\nTRAIN_DIR = \"/kaggle/input/diabetic-retinopathy-train-unzipped/train\"\n\nwidths = []\nheights = []\n\n# On analyse un échantillon pour aller vite\nfor row in df_train.sample(500).itertuples():\n    img_path = f\"{TRAIN_DIR}/{row.image}.jpeg\"\n    if not os.path.exists(img_path):\n        continue\n    img = Image.open(img_path)\n    w, h = img.size\n    widths.append(w)\n    heights.append(h)\n\nprint(\"Largeur moyenne :\", np.mean(widths))\nprint(\"Hauteur moyenne :\", np.mean(heights))\n\nplt.hist(widths, bins=30)\nplt.title(\"Distribution des largeurs\")\nplt.show()\n\nplt.hist(heights, bins=30)\nplt.title(\"Distribution des hauteurs\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-02T06:19:03.464447Z","iopub.execute_input":"2026-08-02T06:19:03.464881Z","iopub.status.idle":"2026-08-02T06:19:13.302017Z","shell.execute_reply.started":"2026-08-02T06:19:03.464848Z","shell.execute_reply":"2026-08-02T06:19:13.300635Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"🧠 Pourquoi cette analyse est utile ?\nElle te donne :\n\n✔ La taille moyenne des images\nSouvent ~2000×2000 px → trop grand pour un modèle performant.\n\n✔ La variabilité des résolutions\nCertaines images sont :\n\n1500×1500\n\n3000×3000\n\nparfois 4000×4000\n\n👉 Cela justifie un prétraitement obligatoire :\n\n🔥 Crop du cercle (Ben Graham)\n→ supprime les bordures noires\n→ réduit la taille\n→ augmente la qualité visuelle\n→ +0.05 à +0.10 de score Kaggle\n\n🔥 Resize 512×512\n→ standardise les résolutions\n→ accélère l’entraînement\n→ améliore la stabilité du modèle\n→ +0.03 à +0.05 de score","metadata":{}},{"cell_type":"code","source":"# Détection d'images floues\n\nimport cv2\n\ndef blur_score(img):\n    gray = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2GRAY)\n    return cv2.Laplacian(gray, cv2.CV_64F).var()\n\nblur_values = []\n\nfor row in df_train.sample(500).itertuples():\n    img_path = f\"{TRAIN_DIR}/{row.image}.jpeg\"\n    img = Image.open(img_path).convert(\"RGB\")\n    blur_values.append(blur_score(img))\n\nplt.hist(blur_values, bins=30)\nplt.title(\"Distribution du flou (variance du Laplacien)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-02T06:15:03.536358Z","iopub.execute_input":"2026-08-02T06:15:03.537040Z","iopub.status.idle":"2026-08-02T06:16:34.092030Z","shell.execute_reply.started":"2026-08-02T06:15:03.537001Z","shell.execute_reply":"2026-08-02T06:16:34.090778Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Pré-traitement\nimport cv2\nimport numpy as np\nimport os\nfrom tqdm import tqdm\nfrom concurrent.futures import ProcessPoolExecutor\n\nTRAIN_DIR = \"/kaggle/input/diabetic-retinopathy-train-unzipped/train\"\nOUT_DIR = \"/kaggle/working/preprocessed_train\"\nos.makedirs(OUT_DIR, exist_ok=True)\n\n# --- 1. Crop circulaire (Ben Graham) ---\ndef crop_circle(img):\n    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    h, w = gray.shape\n    mask = np.zeros_like(gray)\n    cv2.circle(mask, (w//2, h//2), min(w,h)//2, 255, -1)\n    cropped = cv2.bitwise_and(img, img, mask=mask)\n    return cropped\n\n# --- 2. CLAHE (LAB) ---\nclahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))\n\ndef apply_clahe(img):\n    lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)\n    l, a, b = cv2.split(lab)\n    l2 = clahe.apply(l)\n    lab = cv2.merge((l2, a, b))\n    clahe_img = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)\n    return clahe_img\n\n# --- 3. Sharpen léger ---\nkernel_sharp = np.array([[0, -1, 0],\n                         [-1, 5, -1],\n                         [0, -1, 0]])\n\ndef sharpen(img):\n    sharp = cv2.filter2D(img, -1, kernel_sharp)\n    return sharp\n\n# --- 4. Flou léger probabiliste ---\ndef gaussian_blur(img):\n    return cv2.GaussianBlur(img, (3,3), 0)\n\n# --- 5. Gamma correction ---\ndef gamma_correct(img, gamma=1.2):\n    inv_gamma = 1.0 / gamma\n    table = np.array([(i / 255.0) ** inv_gamma * 255 for i in range(256)]).astype(\"uint8\")\n    corrected = cv2.LUT(img, table)\n    return corrected\n\n# --- 6. Resize 512x512 ---\ndef resize(img):\n    return cv2.resize(img, (512, 512))\n\n# --- Pipeline complet avec logs optionnels ---\ndef preprocess(img, debug=False, img_name=None):\n    if debug and img_name:\n        print(f\"\\n=== Prétraitement de {img_name} ===\")\n        print(f\"Étape 0 : taille initiale = {img.shape[1]}x{img.shape[0]}\")\n\n    img = crop_circle(img)\n    if debug:\n        print(f\"Étape 1 : après crop cercle → taille = {img.shape[1]}x{img.shape[0]}\")\n\n    img = apply_clahe(img)\n    if debug:\n        print(\"Étape 2 : CLAHE appliqué\")\n\n    img = sharpen(img)\n    if debug:\n        print(\"Étape 3 : sharpen léger appliqué\")\n\n    img = gamma_correct(img, gamma=1.2)\n    if debug:\n        print(\"Étape 4 : gamma correction (1.2) appliquée\")\n\n    if np.random.rand() < 0.3:\n        img = gaussian_blur(img)\n        if debug:\n            print(\"Étape 5 : flou gaussien léger appliqué (p=0.3)\")\n    else:\n        if debug:\n            print(\"Étape 5 : flou gaussien non appliqué\")\n\n    img = resize(img)\n    if debug:\n        print(f\"Étape 6 : resize final → 512x512\")\n\n    return img\n\n# --- Traitement d'une image ---\ndef process_one_image(img_name, debug=False):\n    path = os.path.join(TRAIN_DIR, f\"{img_name}.jpeg\")\n    if not os.path.exists(path):\n        if debug:\n            print(f\"[SKIP] {img_name} : fichier introuvable\")\n        return\n    \n    img = cv2.imread(path)\n    if img is None:\n        if debug:\n            print(f\"[SKIP] {img_name} : image non lisible\")\n        return\n    \n    img = preprocess(img, debug=debug, img_name=img_name)\n    out_path = f\"{OUT_DIR}/{img_name}.jpeg\"\n    cv2.imwrite(out_path, img)\n    if debug:\n        print(f\"[OK] {img_name} : sauvegardée dans {out_path}\")\n\n# --- 1) Mode DEBUG : tester sur quelques images ---\ndebug_names = [f.replace('.jpeg', '') for f in os.listdir(TRAIN_DIR)[:5]]\nprint(\"=== MODE DEBUG : test sur 5 images ===\")\nfor name in debug_names:\n    process_one_image(name, debug=True)\n\nprint(\"\\n=== DEBUG terminé. Si tout est OK, on lance le traitement complet. ===\")\n\n# --- 2) Traitement complet en parallèle ---\nimage_names = [f.replace('.jpeg', '') for f in os.listdir(TRAIN_DIR)]\nprint(f\"\\n=== Lancement du prétraitement complet sur {len(image_names)} images ===\")\n\nwith ProcessPoolExecutor(max_workers=4) as executor:\n    list(tqdm(executor.map(process_one_image, image_names), total=len(image_names)))\n\nprint(\"\\n=== Prétraitement terminé. Les images sont dans :\", OUT_DIR)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-02T08:12:31.241126Z","iopub.execute_input":"2026-08-02T08:12:31.241500Z","iopub.status.idle":"2026-08-02T10:23:06.334946Z","shell.execute_reply.started":"2026-08-02T08:12:31.241467Z","shell.execute_reply":"2026-08-02T10:23:06.330088Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"paths_train= glob('/kaggle/input/diabetic-retinopathy-train-unzipped/train/*.jpeg')\nimage=cv2.imread(paths_train[0])\nplt.imshow(image)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-08-02T10:42:14.495306Z","iopub.execute_input":"2026-08-02T10:42:14.495742Z"}},"outputs":[],"execution_count":null}]}