{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","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":14774,"databundleVersionId":875431,"sourceType":"competition"},{"sourceId":10739807,"sourceType":"datasetVersion","datasetId":6659655},{"sourceId":10875759,"sourceType":"datasetVersion","datasetId":6757456}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport plotly.express as px\nimport cv2\n\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, Activation, Dropout, BatchNormalization, GlobalAveragePooling2D, Lambda\nfrom tensorflow.keras.callbacks import EarlyStopping\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix, classification_report \nimport joblib\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nprint ('modules loaded')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Function to crop the image based on grayscale threshold\ndef top_bottom_hat_filtering(path):\n    image = cv2.imread(path)\n\n    cropped_img = crop_image_from_gray(image)\n   \n   # Elemen struktural (kernel) untuk operasi morfologi\n    kernel_size = 15  # Ukuran kernel harus disesuaikan dengan fitur yang ingin diperjelas\n    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))\n    \n    # Top-Hat transform (menyoroti fitur terang)\n    top_hat = cv2.morphologyEx(cropped_img, cv2.MORPH_TOPHAT, kernel)\n    \n    # Bottom-Hat transform (menyoroti fitur gelap)\n    bottom_hat = cv2.morphologyEx(cropped_img, cv2.MORPH_BLACKHAT, kernel)\n    \n    # Hasil akhir: Menambahkan Top-Hat dan mengurangi Bottom-Hat untuk meningkatkan kontras\n    enhanced_image = cv2.add(cropped_img, top_hat)  # Menonjolkan area terang\n    # enhanced_image = cv2.subtract(enhanced_image, bottom_hat)  # Menghilangkan bayangan gelap\n\n    \n    return image","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image = cv2.imread('/kaggle/input/resized-dataset-aptos/Severe/14e3f84445f7.png')\n\n# Elemen struktural (kernel) untuk operasi morfologi\nkernel_size = 15  # Ukuran kernel harus disesuaikan dengan fitur yang ingin diperjelas\nkernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))\n\n# Top-Hat transform (menyoroti fitur terang)\ntop_hat = cv2.morphologyEx(image, cv2.MORPH_TOPHAT, kernel)\n\n# Bottom-Hat transform (menyoroti fitur gelap)\nbottom_hat = cv2.morphologyEx(image, cv2.MORPH_BLACKHAT, kernel)\n\n# Hasil akhir: Menambahkan Top-Hat dan mengurangi Bottom-Hat untuk meningkatkan kontras\nenhanced_image = cv2.add(image, top_hat)  # Menonjolkan area terang\nenhanced_image2 = cv2.subtract(image, bottom_hat)  # Menghilangkan bayangan gelap\nenhanced_image3 = cv2.subtract(enhanced_image, bottom_hat)  # Menghilangkan bayangan gelap\n\n# Menampilkan hasil\nplt.figure(figsize=(10,5))\nplt.subplot(1,3,1), plt.imshow(image, cmap='gray'), plt.title('Original Image'), plt.axis('off')\nplt.subplot(1,3,2), plt.imshow(top_hat, cmap='gray'), plt.title('Top-Hat Transform'), plt.axis('off')\nplt.subplot(1,3,3), plt.imshow(bottom_hat, cmap='gray'), plt.title('Bottom-Hat Transform'), plt.axis('off')\nplt.show()\n\nplt.figure(figsize=(10,10))\nplt.subplot(1,4,1), plt.imshow(image, cmap='gray'), plt.title('Original Image'), plt.axis('off')\nplt.subplot(1,4,2), plt.imshow(enhanced_image, cmap='gray'), plt.title('Top-Hat '), plt.axis('off')\nplt.subplot(1,4,3), plt.imshow(enhanced_image2, cmap='gray'), plt.title('Bottom-Hat '), plt.axis('off')\nplt.subplot(1,4,4), plt.imshow(enhanced_image3, cmap='gray'), plt.title('Top-Bottom '), plt.axis('off')\nplt.show()\n\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import os\n# import cv2\n# import pandas as pd\n# import shutil\n\n# # Dictionary mapping dari int ke label string\n# label_dict = {\n#     0: \"No DR\",\n#     1: \"Mild\",\n#     2: \"Moderate\",\n#     3: \"Severe\",\n#     4: \"Proliferative DR\"\n# }\n\n# # Path ke file CSV dan folder gambar input\n# csv_path = '/kaggle/input/aptos2019-blindness-detection/train.csv'\n# input_folder = '/kaggle/input/aptos2019-blindness-detection/train_images'  # Pastikan folder ini ada di Kaggle working directory\n\n# # Folder output yang akan menyimpan gambar yang sudah diproses\n# output_folder = '/kaggle/working/resized_aptos_512'\n\n# # Buat folder output jika belum ada\n# if not os.path.exists(output_folder):\n#     os.makedirs(output_folder)\n\n# # Baca file CSV\n# df = pd.read_csv(csv_path)\n\n# # Loop setiap baris dalam CSV\n# for index, row in df.iterrows():\n#     filename = row['id_code']\n    \n#     # Tambahkan ekstensi .png jika belum ada\n#     if not os.path.splitext(filename)[1]:\n#         filename += \".png\"\n    \n#     # Ubah label dari int ke string menggunakan dictionary\n#     int_label = int(row['diagnosis'])\n#     label_str = label_dict.get(int_label, \"Unknown\")\n    \n#     # Buat folder untuk label jika belum ada\n#     label_folder = os.path.join(output_folder, label_str)\n#     if not os.path.exists(label_folder):\n#         os.makedirs(label_folder)\n    \n#     # Path lengkap ke file gambar input\n#     image_path = os.path.join(input_folder, filename)\n    \n#     # Baca gambar menggunakan OpenCV\n#     image = cv2.imread(image_path)\n#     if image is None:\n#         print(f\"Warning: Gambar {filename} tidak dapat dibaca.\")\n#         continue\n    \n#     # Resize gambar ke 512x512 piksel\n#     resized_image = cv2.resize(image, (512, 512))\n    \n#     # Tentukan path output untuk gambar yang sudah diproses\n#     output_path = os.path.join(label_folder, filename)\n    \n#     # Simpan gambar yang sudah di-resize\n#     cv2.imwrite(output_path, resized_image)\n\n# print(\"Proses pembuatan dataset selesai!\")\n\n# # Setelah dataset selesai dibuat, zip folder output\n# archive_name = \"aptos_512\"  # nama file zip tanpa ekstensi\n# shutil.make_archive(archive_name, 'zip', output_folder)\n# print(f\"Folder {output_folder} telah di-zip menjadi {archive_name}.zip\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Function to crop the image based on grayscale threshold\ndef crop_image_from_gray(img, tol=7):\n    if img.ndim == 2:\n        mask = img > tol\n        return img[np.ix_(mask.any(1), mask.any(0))]\n    elif img.ndim == 3:\n        gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n        mask = gray_img > tol\n        \n        check_shape = img[:,:,0][np.ix_(mask.any(1), mask.any(0))].shape[0]\n        if check_shape == 0:  # Image is too dark so that we crop out everything\n            return img  # Return original image\n        else:\n            img1 = img[:,:,0][np.ix_(mask.any(1), mask.any(0))]\n            img2 = img[:,:,1][np.ix_(mask.any(1), mask.any(0))]\n            img3 = img[:,:,2][np.ix_(mask.any(1), mask.any(0))]\n            img = np.stack([img1, img2, img3], axis=-1)\n        return img\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocessing_clahe_rgb(path):\n    image = cv2.imread(path)\n\n    # Crop the image based on gray threshold\n    image_cropped = crop_image_from_gray(image)\n    \n    image_rgb = cv2.cvtColor(image_cropped, cv2.COLOR_BGR2RGB)\n\n    # Split the image into its channels (BGR format)\n    blue, green, red = cv2.split(image_rgb)\n    \n    clahe = cv2.createCLAHE(clipLimit=4.0, tileGridSize=(4, 4))\n\n    # Apply CLAHE to all three channels\n    blue_clahe = clahe.apply(blue)\n    green_clahe = clahe.apply(green)\n    red_clahe = clahe.apply(red)\n    \n    # Merge the CLAHE-enhanced channels back together\n    result_image = cv2.merge([red, green_clahe, blue])\n\n    return result_image","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\n\ndef preprocessing_clahe(path, clip_limit=4.0, grid_size=(4, 4)):\n    image = cv2.imread(path)\n\n    image_cropped = crop_image_from_gray(image)\n\n    # Konversi ke LAB\n    lab_image = cv2.cvtColor(image_cropped, cv2.COLOR_RGB2Lab)\n    \n    # Pisahkan channel L, A, dan B\n    l_channel, a_channel, b_channel = cv2.split(lab_image)\n    \n    # Terapkan CLAHE pada L-channel\n    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=grid_size)\n    \n    l_channel_clahe = clahe.apply(l_channel)\n\n    # Gabungkan kembali L-channel yang telah diproses dengan A dan B yang asli\n    lab_image_clahe = cv2.merge((l_channel_clahe, a_channel, b_channel))\n    \n    # Konversi kembali ke RGB\n    image_clahe = cv2.cvtColor(lab_image_clahe, cv2.COLOR_Lab2RGB)\n\n    \n    return image\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocessing_clahe_grayscale(path, clip_limit=4.0, grid_size=(4, 4)):\n    # Baca gambar dalam mode grayscale\n    image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)\n    \n    # Buat objek CLAHE dengan clip limit dan ukuran grid yang diberikan\n    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=grid_size)\n    \n    # Terapkan CLAHE pada gambar grayscale\n    image_clahe = clahe.apply(image)\n    \n    return image_clahe","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nfrom tqdm import tqdm\nimport shutil\n\n# Misal, data_dir adalah folder dataset asli\ndata_dir = \"/kaggle/input/resized-dataset-aptos\"  \noutput_dir = \"/kaggle/working/preprocessed\"\n\n# Jika folder output sudah ada, hapus terlebih dahulu\nif os.path.exists(output_dir):\n    shutil.rmtree(output_dir)\n\nos.makedirs(output_dir, exist_ok=True)\n\n# Fungsi untuk menyimpan gambar hasil preprocessing ke folder output berdasarkan kelas baru\ndef save_image(image, new_label, filename):\n    # Pastikan folder untuk new_label sudah ada di output_dir\n    label_dir = os.path.join(output_dir, new_label)\n    os.makedirs(label_dir, exist_ok=True)  # Buat folder jika belum ada\n    image_path = os.path.join(label_dir, filename)\n    cv2.imwrite(image_path, image)\n\n# Looping melalui setiap folder di dataset\nfor label in os.listdir(data_dir):\n    label_dir = os.path.join(data_dir, label)\n    \n    # Tentukan kelas baru: jika label == \"No_DR\", maka new_label = \"0\"; selain itu new_label = \"1\"\n    new_label = \"0\" if label == \"No_DR\" else \"1\"\n    \n    if os.path.isdir(label_dir):\n        for filename in tqdm(os.listdir(label_dir), desc=f\"Processing {label}\"):\n            # Tambahkan ekstensi .png jika belum ada\n            if not os.path.splitext(filename)[1]:\n                filename += \".png\"\n                \n            image_path = os.path.join(label_dir, filename)\n            processed_image = top_bottom_hat_filtering(image_path)\n            if processed_image is not None:\n                save_image(processed_image, new_label, filename)\n\n# Mengompres folder hasil preprocessing menjadi file ZIP\nshutil.make_archive(\"/kaggle/working/preprocessed_resized\", \"zip\", output_dir)\n\nprint(\"Preprocessing selesai dan file zip sudah dibuat!\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import os\n# import cv2\n# import numpy as np\n# from tqdm import tqdm\n# import shutil\n\n# # data_dir = \"/kaggle/input/resized-dataset-aptos\"  \n# output_dir = \"/kaggle/working/preprocessed\"\n\n# if os.path.exists(output_dir):\n#     shutil.rmtree(output_dir)\n\n# os.makedirs(output_dir, exist_ok=True)\n\n# # Fungsi untuk menyimpan gambar hasil preprocessing\n# def save_image(image, label, filename):\n#     # Pastikan folder untuk label sudah ada di output_dir\n#     label_dir = os.path.join(output_dir, label)\n#     os.makedirs(label_dir, exist_ok=True)  # Buat folder label jika belum ada\n#     image_path = os.path.join(label_dir, filename)\n#     cv2.imwrite(image_path, image)\n\n# # Looping melalui setiap folder label dalam dataset\n# for label in os.listdir(data_dir):\n#     label_dir = os.path.join(data_dir, label)\n    \n#     if os.path.isdir(label_dir):  # Hanya proses folder, bukan file\n#         for filename in tqdm(os.listdir(label_dir), desc=f\"Processing {label}\"):\n#             image_path = os.path.join(label_dir, filename)\n\n#             # Preprocess gambar\n#             processed_image = preprocessing_clahe(image_path)\n\n#             if processed_image is not None:\n#                 save_image(processed_image, label, filename)\n\n# # Mengompres folder hasil preprocessing menjadi file ZIP\n# shutil.make_archive(\"/kaggle/working/preprocessed_resized\", \"zip\", output_dir)\n\n# print(\"Preprocessing selesai dan file zip sudah dibuat!\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\n\n# Membaca dataset dan membagi menjadi train dan validation\ntrain_dataset = tf.keras.preprocessing.image_dataset_from_directory(\n    output_dir,\n    validation_split=0.2,  # 80% untuk train, 20% untuk validasi\n    subset=\"training\",\n    seed=12,\n    image_size=(224,224),\n    batch_size=32\n)\n\nval_dataset = tf.keras.preprocessing.image_dataset_from_directory(\n    output_dir,\n    validation_split=0.2,  # Sesuaikan dengan split yang sama\n    subset=\"validation\",\n    seed=12,\n    image_size=(224, 224),\n    batch_size=32\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class_names = train_dataset.class_names\n\n# Menghitung jumlah gambar per kelas\nclass_counts = {class_name: 0 for class_name in class_names}\nfor images, labels in train_dataset:\n    for label in labels.numpy():\n        class_counts[class_names[label]] += 1\n\nprint(\"Jumlah gambar per kelas:\", class_counts)\nfor class_name, count in class_counts.items():\n    print(f\"{class_name}: {count}\")\n\n# Membuat diagram batang (bar chart)\nplt.figure(figsize=(10, 6))\nplt.bar(class_counts.keys(), class_counts.values(), color='skyblue')\nplt.title('Jumlah Gambar per Kelas di train_dataset')\nplt.xlabel('Kelas')\nplt.ylabel('Jumlah Gambar')\nplt.xticks(rotation=45)\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Data augmentation layer\ndata_augmentation_layer = tf.keras.Sequential([\n    tf.keras.layers.RandomFlip(\"horizontal_and_vertical\"),  # Horizontal & Vertical Flip\n    tf.keras.layers.RandomRotation(0.2, fill_mode=\"constant\"),  # Rotasi dengan fill_mode constant\n    tf.keras.layers.Lambda(lambda x: tf.image.random_brightness(x, max_delta=0.2)),  # Brightness augmentation\n    tf.keras.layers.Lambda(lambda x: tf.image.random_contrast(x, lower=0.8, upper=1.2))  # Contrast augmentation\n])\n\n# Terapkan augmentation pada dataset training\naugmented_train = train_dataset.map(lambda x, y: (data_augmentation_layer(x), y))\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import tensorflow as tf\n# import cv2\n# import numpy as np\n\n# # Fungsi kustom untuk mengaplikasikan blur atau sharpen secara acak pada batch gambar\n# def apply_random_blur_sharpen(images):\n#     # images: numpy array dengan shape (batch, height, width, channels)\n#     out_images = []\n#     # Pastikan nilai gambar berada pada rentang 0-255 (dtype uint8) untuk operasi cv2\n#     images = images.astype(np.uint8)\n#     for img in images:\n#         # Pilih secara acak apakah akan blur, sharpen, atau tidak mengubah\n#         choice = np.random.choice(['blur', 'sharpen', 'none'])\n#         if choice == 'blur':\n#             # Gunakan Gaussian Blur dengan kernel 5x5\n#             out_img = cv2.GaussianBlur(img, (5,5), 0)\n#         elif choice == 'sharpen':\n#             # Kernel sharpening standar\n#             kernel = np.array([[0, -1, 0],\n#                                [-1, 5, -1],\n#                                [0, -1, 0]])\n#             out_img = cv2.filter2D(img, -1, kernel)\n#         else:\n#             out_img = img\n#         out_images.append(out_img)\n#     return np.array(out_images)\n\n# # Data augmentation layer yang mengintegrasikan parameter:\n# # contrast_range=0.2, brightness_range=20., hue_range=10.,\n# # saturation_range=20., blur_and_sharpen=True, rotate_range=180.,\n# # scale_range=0.2, shear_range=0.2, shift_range=0.2, do_mirror=True.\n# data_augmentation_layer = tf.keras.Sequential([\n#     # Mirror: do_mirror=True\n#     tf.keras.layers.RandomFlip(\"horizontal_and_vertical\"),\n#     # Rotation: rotate_range=180 deg -> factor 0.5 (180/360)\n#     tf.keras.layers.RandomRotation(0.5, fill_mode=\"constant\"),\n#     # Scale: scale_range=0.2 -> RandomZoom, factor negatif berarti zoom out\n#     tf.keras.layers.RandomZoom(height_factor=(-0.2, 0.2), width_factor=(-0.2, 0.2), fill_mode=\"constant\"),\n#     # Shift: shift_range=0.2 -> RandomTranslation\n#     tf.keras.layers.RandomTranslation(height_factor=0.2, width_factor=0.2, fill_mode=\"constant\"),\n#     # Contrast: contrast_range=0.2\n#     tf.keras.layers.RandomContrast(0.2),\n#     # Brightness: brightness_range=20. (asumsi gambar dalam [0,1], 20/255 ~ 0.078)\n#     tf.keras.layers.Lambda(lambda x: tf.image.random_brightness(x, max_delta=20./255.0)),\n#     # Hue: hue_range=10 deg -> 10/360 ~ 0.0278\n#     tf.keras.layers.Lambda(lambda x: tf.image.random_hue(x, max_delta=10./360.0)),\n#     # Saturation: saturation_range=20% -> lower=0.8, upper=1.2\n#     tf.keras.layers.Lambda(lambda x: tf.image.random_saturation(x, lower=0.8, upper=1.2)),\n#     # Shear: shear_range=0.2, menggunakan tfa.image.transform\n#     tf.keras.layers.Lambda(lambda x: tfa.image.transform(\n#         x,\n#         transforms=tf.random.uniform((tf.shape(x)[0], 8), minval=-0.2, maxval=0.2),\n#         interpolation='BILINEAR',\n#         fill_mode='CONSTANT'\n#     )),\n#     # Blur and sharpen: menggunakan fungsi kustom dengan tf.numpy_function\n#     tf.keras.layers.Lambda(lambda x: tf.numpy_function(\n#         func=apply_random_blur_sharpen,\n#         inp=[x],\n#         Tout=x.dtype\n#     ))\n# ])\n\n# # Terapkan augmentation pada dataset training\n# augmented_train = train_dataset.map(lambda x, y: (data_augmentation_layer(x), y))\n\n# # Contoh: Jika ingin melihat output augmentasi\n# # Untuk satu batch dari augmented_train, misalnya:\n# for batch_images, batch_labels in augmented_train.take(1):\n#     # Tampilkan gambar pertama dalam batch\n#     import matplotlib.pyplot as plt\n#     plt.imshow(tf.cast(batch_images[0], tf.uint8).numpy())\n#     plt.title(\"Augmented Image Example\")\n#     plt.axis(\"off\")\n#     plt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Ambil satu batch gambar dan label menggunakan iterator\ntrain_iterator = iter(train_dataset)\nimages, labels = next(train_iterator)\n\n# Pastikan gambar ada dalam range [0,1], lalu ubah ke [0,255] untuk ditampilkan\nfig, axes = plt.subplots(2, 4, figsize=(8, 8))  # Menyesuaikan figsize lebih kecil agar gambar lebih rapat\nfor i in range(8):\n    row = i // 4  # Menentukan baris (0 hingga 3)\n    col = i % 4   # Menentukan kolom (0 hingga 3)\n    \n    axes[row, col].imshow(images[i] / 255)  # Skala ulang agar bisa ditampilkan\n    axes[row, col].axis(\"off\")\n\n# Menyesuaikan layout agar gambar tidak tumpang tindih\nplt.tight_layout(pad=0.5)\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for images, labels in augmented_train.take(2):  # Taking one batch\n    plt.figure(figsize=(10, 10))\n\n    # Loop over the first 16 images in the batch\n    for i in range(8):\n        plt.subplot(4, 4, i + 1)\n        plt.imshow(images[i] / 255 )\n        plt.axis(\"off\")\n    \n    plt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# from sklearn.utils.class_weight import compute_class_weight\n# # Ambil label dari train_dataset\n# y_train = np.concatenate([y for x, y in train_dataset], axis=0)  # Gabungkan semua label\n\n# # Jika label one-hot encoded, konversi ke integer\n# if y_train.ndim > 1:\n#     y_train = np.argmax(y_train, axis=1)\n\n# # Hitung class weight\n# class_weights = compute_class_weight(\n#     class_weight='balanced',  # Menghitung bobot secara otomatis\n#     classes=np.unique(y_train),  # Kelas unik dalam dataset\n#     y=y_train  # Label training\n# )\n\n# class_weights_dict = dict(enumerate(class_weights))\n# print(\"Class Weights:\", class_weights_dict)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# val_batches = tf.data.experimental.cardinality(val_normalization_dataset)\nval_batches = tf.data.experimental.cardinality(val_dataset)\ntest_batches = val_batches // 2 \nval_batches = val_batches - test_batches  \n\ntest_dataset = val_dataset.take(test_batches)\nval_dataset = val_dataset.skip(test_batches)\n# test_normalization_dataset = val_normalization_dataset.take(test_batches)\n# val_normalization_dataset = val_normalization_dataset.skip(test_batches)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Prefetch untuk performa lebih baik\nAUTOTUNE = tf.data.AUTOTUNE\ntrain_gen = augmented_train.prefetch(buffer_size=AUTOTUNE)\nval_gen = val_dataset.prefetch(buffer_size=AUTOTUNE)\ntest_gen = test_dataset.prefetch(buffer_size=AUTOTUNE)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nimport cv2\nimport numpy as np\nfrom tensorflow.keras import regularizers\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Input, Dense, Dropout, GlobalAveragePooling2D\nfrom tensorflow.keras.optimizers import Adamax, Adam\n\nrescale_layer = tf.keras.layers.Rescaling(1./255)\n\n# Define the model using Functional API\ninputs = Input(shape=(224, 224, 3))\n\n# x = preprocess_input(inputs)\n# x = rescale_layer(inputs)\n\nbase_model = tf.keras.applications.ConvNeXtTiny(include_top=False,\n    weights=\"imagenet\",\n    input_shape=(224, 224, 3),\n)\n\nbase_model.trainable = True\n    \nx = base_model(inputs)\n\nx = Dropout(0.5)(x)\n\nx = GlobalAveragePooling2D()(x)\n\n# # # Fully connected layer with regularization\nx = Dense(256, activation='relu', kernel_regularizer=regularizers.l2(0.01))(x)\nx = BatchNormalization()(x)\n\nx = Dropout(0.5)(x)\n\n# Output layer (4 classes)\noutputs = Dense(1, activation='sigmoid')(x)\n\n# Define the model\nmodel = Model(inputs=inputs, outputs=outputs)\n\n# Compile the model\nmodel.compile(optimizer=Adam(learning_rate=0.0001), loss=tf.keras.losses.BinaryCrossentropy(label_smoothing=0.1), metrics=['accuracy'])\n\n# Model summary\nmodel.summary()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import tensorflow as tf\n# from tensorflow.keras import layers, Model\n\n# def ConvNeXtBlock(x, filters, drop_path_rate=0.0):\n#     \"\"\"\n#     ConvNeXt Block: Combines depthwise convolution, layer normalization, and GELU activation.\n#     \"\"\"\n#     # Save the input for residual connection\n#     residual = x\n\n#     # Depthwise Convolution\n#     x = layers.DepthwiseConv2D(kernel_size=7, padding=\"same\")(x)\n#     x = layers.LayerNormalization(epsilon=1e-6)(x)\n\n#     # Pointwise Convolution (1x1 Conv to expand/reduce channels)\n#     x = layers.Conv2D(filters=filters, kernel_size=1, strides=1, padding=\"same\")(x)\n#     x = layers.Activation(\"gelu\")(x)\n\n#     # Pointwise Convolution (1x1 Conv to restore channels)\n#     x = layers.Conv2D(filters=filters, kernel_size=1, strides=1, padding=\"same\")(x)\n\n#     # Drop Path (Stochastic Depth)\n#     if drop_path_rate > 0.0:\n#         x = layers.Dropout(drop_path_rate)(x)\n\n#     # Add residual connection\n#     x = layers.Add()([residual, x])\n\n#     return x\n\n# def ConvNeXtStem(x, filters):\n#     \"\"\"\n#     Stem block for ConvNeXt: Initial downsampling and feature extraction.\n#     \"\"\"\n#     x = layers.Conv2D(filters=filters, kernel_size=4, strides=4, padding=\"same\")(x)\n#     x = layers.LayerNormalization(epsilon=1e-6)(x)\n#     return x\n\n# def ConvNeXtStage(x, filters, num_blocks, drop_path_rate=0.0):\n#     \"\"\"\n#     ConvNeXt Stage: A sequence of ConvNeXt blocks.\n#     \"\"\"\n#     for _ in range(num_blocks):\n#         x = ConvNeXtBlock(x, filters, drop_path_rate)\n#     return x\n\n# def ConvNeXt(input_shape=(224, 224, 3), depths=[3, 3, 9, 3], dims=[96, 192, 384, 768], drop_path_rate=0.0):\n#     \"\"\"\n#     ConvNeXt Model: Full architecture with multiple stages.\n#     \"\"\"\n#     inputs = layers.Input(shape=input_shape)\n\n#     # Stem\n#     x = ConvNeXtStem(inputs, dims[0])\n\n#     # Stages\n#     for i, (depth, dim) in enumerate(zip(depths, dims)):\n#         x = ConvNeXtStage(x, dim, depth, drop_path_rate)\n#         if i < len(depths) - 1:  # Downsample between stages\n#             x = layers.Conv2D(filters=dims[i + 1], kernel_size=2, strides=2, padding=\"same\")(x)\n#             x = layers.LayerNormalization(epsilon=1e-6)(x)\n\n#     # Global Average Pooling and Classifier\n#     x = layers.GlobalAveragePooling2D()(x)\n#     x = layers.LayerNormalization(epsilon=1e-6)(x)\n#     outputs = layers.Dense(1, activation=\"sigmoid\")(x)\n\n#     # Create model\n#     model = Model(inputs, outputs, name=\"ConvNeXt\")\n#     return model\n\n# # Create the ConvNeXt model\n# model = ConvNeXt(input_shape=(224, 224, 3))\n# model.compile(optimizer=Adam(learning_rate=0.0001), loss=tf.keras.losses.BinaryCrossentropy(label_smoothing=0.1), metrics=[\"accuracy\"])\n\n# model.summary()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import tensorflow as tf\n# from tensorflow.keras import layers, models\n\n# def build_cnn(input_shape=(224, 224, 3)):\n#     \"\"\"\n#     Membangun model CNN sederhana untuk klasifikasi gambar.\n    \n#     Args:\n#         input_shape (tuple): Bentuk input gambar (height, width, channels).\n#         num_classes (int): Jumlah kelas output.\n    \n#     Returns:\n#         model: Model CNN.\n#     \"\"\"\n#     # Input layer\n#     inputs = layers.Input(shape=input_shape)\n\n#     # Convolutional Block 1\n#     x = layers.Conv2D(32, (3, 3), padding=\"same\", activation=\"relu\")(inputs)\n#     x = layers.MaxPooling2D((2, 2))(x)\n\n#     # Convolutional Block 2\n#     x = layers.Conv2D(64, (3, 3), padding=\"same\", activation=\"relu\")(x)\n#     x = layers.MaxPooling2D((2, 2))(x)\n\n#     # Convolutional Block 3\n#     x = layers.Conv2D(128, (3, 3), padding=\"same\", activation=\"relu\")(x)\n#     x = layers.MaxPooling2D((2, 2))(x)\n\n#     # Fully Connected Layers\n#     x = layers.Flatten()(x)\n#     x = layers.Dense(256, activation=\"relu\")(x)\n#     x = layers.Dropout(0.5)(x)  # Dropout untuk mengurangi overfitting\n#     outputs = layers.Dense(1, activation=\"sigmoid\")(x)\n\n#     # Membuat model\n#     model = models.Model(inputs, outputs, name=\"CNN\")\n#     return model\n\n# # Membangun model\n# model = build_cnn(input_shape=(224, 224, 3))\n# model.compile(optimizer=Adam(learning_rate=0.0001), loss=tf.keras.losses.BinaryCrossentropy(label_smoothing=0.1), metrics=[\"accuracy\"])\n\n# model.summary()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping\n\nearly_stopping = EarlyStopping(monitor='val_loss',patience=5,restore_best_weights=True)\n\n# ReduceLROnPlateau: Mengurangi learning rate jika val_loss tidak membaik\nreduce_lr = ReduceLROnPlateau(\n    monitor='val_loss',  # Memantau 'val_loss'\n    factor=0.5,          # Mengurangi learning rate sebesar 50%\n    patience=3,          # Menunggu 2 epoch sebelum mengurangi learning rate\n    min_lr=1e-5,         # Nilai learning rate terendah\n    verbose=1            # Menampilkan pesan ketika learning rate diubah\n)\n\nhistory=model.fit(train_gen,epochs=50,\n                  validation_data=val_gen,shuffle=True,\n                  callbacks=[reduce_lr,early_stopping],\n                 )","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\n# Data dari history training\ntr_acc = history.history['accuracy']\ntr_loss = history.history['loss']\nval_acc = history.history['val_accuracy']\nval_loss = history.history['val_loss']\n\n# Mencari epoch terbaik\nindex_loss = np.argmin(val_loss)\nval_lowest = val_loss[index_loss]\nindex_acc = np.argmax(val_acc)\nacc_highest = val_acc[index_acc]\n\n# Label untuk plot\nEpochs = [i+1 for i in range(len(tr_acc))]\nloss_label = f'best epoch= {str(index_loss + 1)}'\nacc_label = f'best epoch= {str(index_acc + 1)}'\n\n# Plotting\nplt.figure(figsize=(20, 8))\nplt.style.use('fivethirtyeight')\n\n# Plot Loss\nplt.subplot(1, 2, 1)\nplt.plot(Epochs, tr_loss, 'r', label='Training loss')\nplt.plot(Epochs, val_loss, 'g', label='Validation loss')\nplt.scatter(index_loss + 1, val_lowest, s=150, c='blue', label=loss_label)\nplt.title('Training and Validation Loss')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.ylim(0, max(max(tr_loss), max(val_loss)) * 1.1)  # Set y-axis mulai dari 0\nplt.legend()\n\n# Plot Accuracy\nplt.subplot(1, 2, 2)\nplt.plot(Epochs, tr_acc, 'r', label='Training Accuracy')\nplt.plot(Epochs, val_acc, 'g', label='Validation Accuracy')\nplt.scatter(index_acc + 1, acc_highest, s=150, c='blue', label=acc_label)\nplt.title('Training and Validation Accuracy')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.ylim(0.8, 1)  # Set y-axis mulai dari 0 hingga 1 (karena akurasi antara 0 dan 1)\nplt.legend()\n\n# Simpan plot\nplt.savefig('/kaggle/working/training_plot.png')\n\n# Tampilkan plot\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_score = model.evaluate(test_gen)\n    \nprint(\"Test Loss: \", test_score[0])\nprint(\"Test Accuracy: \", test_score[1])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nfrom sklearn.metrics import confusion_matrix, classification_report, accuracy_score, roc_curve, auc\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Ambil nama kelas dari dataset (misal: [\"No DR\", \"DR\"])\nclass_names = train_dataset.class_names  \n\n# Buat daftar untuk menyimpan label asli, prediksi, dan probabilitas prediksi\ny_true = []\ny_pred = []\ny_pred_proba = []  # Menyimpan probabilitas prediksi untuk ROC curve\n\n# Pastikan model hanya mendukung binary classification (misalnya, No DR vs DR)\nfor images, labels in test_gen:\n    y_true.extend(labels.numpy())  # Label asli dalam format integer\n    \n    # Prediksi menggunakan model\n    predictions = model.predict(images)\n    \n    # Karena pakai sigmoid, output hanya satu neuron -> langsung ambil nilai\n    y_pred_proba.extend(predictions.flatten())  # Ubah ke 1D array\n    y_pred.extend((predictions > 0.5).astype(int).flatten())  # Konversi ke label biner\n\n# Ubah y_true ke binary jika dataset masih multi-class\ny_true = np.array(y_true)\nif len(set(y_true)) > 2:\n    y_true = (y_true > 0).astype(int)  # Pastikan labelnya hanya 0 dan 1\n\n# Menghitung confusion matrix dengan nilai hitungan asli\ncm = confusion_matrix(y_true, y_pred)\n\n# Menghitung akurasi\naccuracy = accuracy_score(y_true, y_pred)\n\n# Menampilkan confusion matrix dengan seaborn\nplt.figure(figsize=(6, 5))\nsns.heatmap(cm, annot=True, fmt=\"d\", xticklabels=class_names, yticklabels=class_names, cmap=\"Blues\", linewidths=.5)\nplt.xlabel('\\nPredicted Label', fontsize=13)\nplt.ylabel('Actual Label\\n', fontsize=13)\nplt.title('Confusion Matrix - Binary', fontsize=15)\nplt.savefig('/kaggle/working/confusion_matrix_binary.png')\nplt.show()\n\n# Menampilkan classification report\nprint(f\"Accuracy: {accuracy:.4f}\")\nprint(classification_report(y_true, y_pred, target_names=class_names))\n\n# Menghitung ROC curve dan AUC untuk binary classification\nfpr, tpr, _ = roc_curve(y_true, y_pred_proba)\nroc_auc = auc(fpr, tpr)\n\n# Plot ROC curve\nplt.figure(figsize=(8, 6))\nplt.plot(fpr, tpr, color='blue', label=f'ROC Curve (AUC = {roc_auc:.2f})')\nplt.plot([0, 1], [0, 1], color='gray', linestyle='--')  # Garis diagonal acak\n\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('ROC Curve - Binary Classification')\nplt.legend(loc='lower right')\nplt.grid(True)\n\n# Simpan gambar\nplt.savefig('/kaggle/working/AUC_ROC_Binary.png')\nplt.show()\nprint(y_true[:10], y_pred[:10], y_pred_proba[:10])\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.save('/kaggle/working/DenseNet121+clahe+dataaug+finetune.h5')","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}