{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.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":5048,"databundleVersionId":868335,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"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\nfor 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","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-08-06T18:41:16.555748Z","iopub.execute_input":"2025-08-06T18:41:16.556012Z","iopub.status.idle":"2025-08-06T18:42:55.943442Z","shell.execute_reply.started":"2025-08-06T18:41:16.555989Z","shell.execute_reply":"2025-08-06T18:42:55.942659Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nimport tensorflow as tf\nimport random\nfrom tensorflow.keras.callbacks import ReduceLROnPlateau\n\n# Suppress TensorFlow logs\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n\n# === CONFIGURATION ===\nINPUT_DIR = \"/kaggle/input/state-farm-distracted-driver-detection/imgs/train\"\nTARGET_SIZE = (224, 224)\nSCALE_TYPE = '[0,1]'  # '[0,1]' or '[-1,1]'\nNUM_CLASSES = 10\nSHOW_TEST_IMAGES = True\nMAX_IMAGES = 10000  # limit for faster load\n\n# === LABELS ===\nLABELS = {\n    'c0': \"safe_driving\", 'c1': \"texting_right\", 'c2': \"talking_on_the_phone_right\",\n    'c3': \"texting_left\", 'c4': \"talking_on_the_phone_left\", 'c5': \"operating_the_radio\",\n    'c6': \"drinking\", 'c7': \"reaching_behind\", 'c8': \"hair_and_makeup\", 'c9': \"talking_to_passenger\"\n}\nLABEL_TO_IDX = {label: idx for idx, label in enumerate(LABELS)}\nIDX_TO_LABEL = {v: k for k, v in LABEL_TO_IDX.items()}\n\n# === LIGHTING ENHANCEMENT WITH PROBABILITY ===\ndef enhance_lighting(img, prob=0.5):\n    \"\"\"Randomly apply CLAHE, gamma correction, and brightness/contrast normalization.\"\"\"\n    if random.random() > prob:\n        return img  # Skip enhancement\n\n    # CLAHE in LAB color space\n    lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)\n    l, a, b = cv2.split(lab)\n\n    # Dynamic clipLimit based on image stats\n    brightness = np.mean(l)\n    contrast = np.std(l)\n    if brightness < 90 and contrast < 40:\n        clip_limit = 3.0\n    elif contrast > 60:\n        clip_limit = 1.2\n    else:\n        clip_limit = 2.0\n\n    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=(8, 8))\n    l = clahe.apply(l)\n\n    lab = cv2.merge((l, a, b))\n    img = cv2.cvtColor(lab, cv2.COLOR_LAB2RGB)\n\n    # Smart gamma correction\n    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n    mean_brightness = np.mean(gray)\n    gamma = np.interp(mean_brightness, [50, 200], [0.6, 1.6])\n    img = np.power(img / 255.0, gamma)\n    img = np.clip(img * 255, 0, 255).astype(np.uint8)\n\n    # Brightness/contrast normalization\n    target_mean, target_std = 128, 64\n    mean, std = cv2.meanStdDev(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY))\n    mean, std = mean[0][0], std[0][0]\n    if abs(mean - target_mean) > 10 or abs(std - target_std) > 5:\n        alpha = target_std / (std + 1e-6)\n        beta = target_mean - mean * alpha\n        img = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)\n\n    return img\n\n# === PREPROCESS IMAGE ===\ndef preprocess_image(img_path, training=True):\n    img = cv2.imread(img_path)\n    if img is None:\n        return None\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    if training:\n        img = enhance_lighting(img, prob=0.5)  # Apply only for training images\n    img = cv2.resize(img, TARGET_SIZE)\n    img = img.astype(np.float32)\n    if SCALE_TYPE == '[0,1]':\n        img /= 255.0\n    elif SCALE_TYPE == '[-1,1]':\n        img = (img / 127.5) - 1.0\n    return img\n\n# === LOAD DATASET ===\ndef load_dataset(input_dir, max_images=None, training=True):\n    X, y = [], []\n    print(f\"📦 Loading {'training' if training else 'test'} images...\")\n    count = 0\n    for label in tqdm(os.listdir(input_dir), desc=\"Classes\"):\n        label_dir = os.path.join(input_dir, label)\n        if not os.path.isdir(label_dir):\n            continue\n        for fname in os.listdir(label_dir):\n            if max_images and count >= max_images:\n                break\n            img_path = os.path.join(label_dir, fname)\n            img = preprocess_image(img_path, training=training)\n            if img is not None:\n                X.append(img)\n                y.append(LABEL_TO_IDX[label])\n                count += 1\n    return np.array(X), np.array(y)\n\n# === VISUALIZE ENHANCEMENT ===\ndef visualize_lighting_effect():\n    sample_dir = os.path.join(INPUT_DIR, 'c0')  # one class folder\n    sample_files = os.listdir(sample_dir)[:5]   # first 5 images\n\n    plt.figure(figsize=(10, 6))\n    for i, fname in enumerate(sample_files):\n        img_path = os.path.join(sample_dir, fname)\n        original = cv2.imread(img_path)\n        original = cv2.cvtColor(original, cv2.COLOR_BGR2RGB)\n\n        enhanced = enhance_lighting(original.copy(), prob=1.0)  # force enhancement\n\n        # Original\n        plt.subplot(2, len(sample_files), i+1)\n        plt.imshow(original)\n        plt.title(\"Original\")\n        plt.axis('off')\n\n        # Enhanced\n        plt.subplot(2, len(sample_files), i+1+len(sample_files))\n        plt.imshow(enhanced)\n        plt.title(\"Enhanced\")\n        plt.axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\n# Preview enhancement before training\nvisualize_lighting_effect()\n\n# === LOAD DATA ===\nX, y = load_dataset(INPUT_DIR, max_images=MAX_IMAGES, training=True)\ny_cat = to_categorical(y, num_classes=NUM_CLASSES)\n\n# === TRAIN-TEST SPLIT ===\nX_train, X_test, y_train, y_test = train_test_split(\n    X, y_cat, test_size=0.2, stratify=y, random_state=42\n)\n\n# === DATA GENERATORS ===\ntrain_gen = ImageDataGenerator(\n    rotation_range=15,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    zoom_range=0.1,\n    horizontal_flip=True\n).flow(X_train, y_train, batch_size=32, shuffle=True)\n\nval_gen = ImageDataGenerator().flow(X_test, y_test, batch_size=32, shuffle=False)\n\n# === MODEL ===\nmodel = tf.keras.Sequential([\n    tf.keras.layers.Input(shape=(224, 224, 3)),\n    tf.keras.layers.Conv2D(32, (3, 3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(),\n    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(),\n    tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),\n    tf.keras.layers.GlobalAveragePooling2D(),\n    tf.keras.layers.Dense(128, activation='relu'),\n    tf.keras.layers.Dense(NUM_CLASSES, activation='softmax')\n])\n\nmodel.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\n\n# === CALLBACKS ===\nreduce_lr = ReduceLROnPlateau(\n    monitor='val_accuracy',\n    factor=0.5,\n    patience=2,\n    verbose=1,\n    min_lr=1e-6\n)\n\n# === TRAIN ===\nprint(\"🚀 Training...\")\nmodel.fit(train_gen, epochs=10, validation_data=val_gen, callbacks=[reduce_lr])\n\n# === EVALUATE ===\nprint(\"\\n📊 Evaluating on test set...\")\nloss, acc = model.evaluate(val_gen)\nprint(f\"✅ Test Accuracy: {acc:.4f}, Test Loss: {loss:.4f}\")\n\n# === SHOW PREDICTIONS ===\nif SHOW_TEST_IMAGES:\n    print(\"\\n🖼️ Showing test predictions...\")\n    X_vis = X_test[:10]\n    y_true = np.argmax(y_test[:10], axis=1)\n    y_pred = np.argmax(model.predict(X_vis), axis=1)\n\n    fig, axes = plt.subplots(2, 5, figsize=(15, 6))\n    for i in range(10):\n        axes[i//5, i%5].imshow(X_vis[i])\n        true_label = LABELS[IDX_TO_LABEL[y_true[i]]]\n        pred_label = LABELS[IDX_TO_LABEL[y_pred[i]]]\n        axes[i//5, i%5].set_title(f\"True: {true_label}\\nPred: {pred_label}\", fontsize=9)\n        axes[i//5, i%5].axis('off')\n    plt.tight_layout()\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-06T18:45:04.237590Z","iopub.execute_input":"2025-08-06T18:45:04.237869Z","iopub.status.idle":"2025-08-06T19:03:44.221395Z","shell.execute_reply.started":"2025-08-06T18:45:04.237846Z","shell.execute_reply":"2025-08-06T19:03:44.220318Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nimport tensorflow as tf\nimport random\nfrom tensorflow.keras.callbacks import ReduceLROnPlateau\n\n# Suppress TensorFlow logs\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n\n# === CONFIGURATION ===\nINPUT_DIR = \"/kaggle/input/state-farm-distracted-driver-detection/imgs/train\"\nTARGET_SIZE = (224, 224)\nSCALE_TYPE = '[0,1]'  # '[0,1]' or '[-1,1]'\nNUM_CLASSES = 10\nSHOW_TEST_IMAGES = True\nMAX_IMAGES = 2000  # reduce for faster run\n\n# === LABELS ===\nLABELS = {\n    'c0': \"safe_driving\", 'c1': \"texting_right\", 'c2': \"talking_on_the_phone_right\",\n    'c3': \"texting_left\", 'c4': \"talking_on_the_phone_left\", 'c5': \"operating_the_radio\",\n    'c6': \"drinking\", 'c7': \"reaching_behind\", 'c8': \"hair_and_makeup\", 'c9': \"talking_to_passenger\"\n}\nLABEL_TO_IDX = {label: idx for idx, label in enumerate(LABELS)}\nIDX_TO_LABEL = {v: k for k, v in LABEL_TO_IDX.items()}\n\n# === INDIVIDUAL STEPS FOR VISUALIZATION ===\ndef apply_CLAHE(img):\n    lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)\n    l, a, b = cv2.split(lab)\n\n    brightness = np.mean(l)\n    contrast = np.std(l)\n    if brightness < 90 and contrast < 40:\n        clip_limit = 3.0\n    elif contrast > 60:\n        clip_limit = 1.2\n    else:\n        clip_limit = 2.0\n\n    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=(8, 8))\n    l = clahe.apply(l)\n    lab = cv2.merge((l, a, b))\n    return cv2.cvtColor(lab, cv2.COLOR_LAB2RGB)\n\ndef apply_gamma(img):\n    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n    mean_brightness = np.mean(gray)\n    gamma = np.interp(mean_brightness, [50, 200], [0.6, 1.6])\n    img = np.power(img / 255.0, gamma)\n    return np.clip(img * 255, 0, 255).astype(np.uint8)\n\ndef apply_brightness_contrast(img):\n    target_mean, target_std = 128, 64\n    mean, std = cv2.meanStdDev(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY))\n    mean, std = mean[0][0], std[0][0]\n    if abs(mean - target_mean) > 10 or abs(std - target_std) > 5:\n        alpha = target_std / (std + 1e-6)\n        beta = target_mean - mean * alpha\n        img = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)\n    return img\n\n# === PROBABILISTIC ENHANCEMENT FOR TRAINING ===\ndef enhance_lighting(img, prob=0.5):\n    if random.random() > prob:\n        return img\n    img = apply_CLAHE(img)\n    img = apply_gamma(img)\n    img = apply_brightness_contrast(img)\n    return img\n\n# === PREPROCESSING ===\ndef preprocess_image(img_path, training=True):\n    img = cv2.imread(img_path)\n    if img is None:\n        return None\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    if training:\n        img = enhance_lighting(img, prob=0.5)\n    img = cv2.resize(img, TARGET_SIZE)\n    img = img.astype(np.float32)\n    if SCALE_TYPE == '[0,1]':\n        img /= 255.0\n    elif SCALE_TYPE == '[-1,1]':\n        img = (img / 127.5) - 1.0\n    return img\n\n# === DATA LOADING ===\ndef load_dataset(input_dir, max_images=None, training=True):\n    X, y = [], []\n    print(f\"📦 Loading {'training' if training else 'test'} images...\")\n    count = 0\n    for label in tqdm(os.listdir(input_dir), desc=\"Classes\"):\n        label_dir = os.path.join(input_dir, label)\n        if not os.path.isdir(label_dir):\n            continue\n        for fname in os.listdir(label_dir):\n            if max_images and count >= max_images:\n                break\n            img_path = os.path.join(label_dir, fname)\n            img = preprocess_image(img_path, training=training)\n            if img is not None:\n                X.append(img)\n                y.append(LABEL_TO_IDX[label])\n                count += 1\n    return np.array(X), np.array(y)\n\n# === VISUALIZE STEPS ===\ndef visualize_lighting_steps():\n    sample_dir = os.path.join(INPUT_DIR, \"c0\")\n    sample_img_path = os.path.join(sample_dir, os.listdir(sample_dir)[0])\n    img = cv2.imread(sample_img_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n    clahe_img = apply_CLAHE(img)\n    clahe_gamma_img = apply_gamma(clahe_img)\n    final_img = apply_brightness_contrast(clahe_gamma_img)\n\n    titles = [\n        \"Original\",\n        \"CLAHE only\",\n        \"CLAHE + Gamma\",\n        \"CLAHE + Gamma + Bright/Contrast\"\n    ]\n    images = [img, clahe_img, clahe_gamma_img, final_img]\n\n    plt.figure(figsize=(15, 5))\n    for i, (title, im) in enumerate(zip(titles, images)):\n        plt.subplot(1, 4, i+1)\n        plt.imshow(im)\n        plt.title(title, fontsize=9)\n        plt.axis(\"off\")\n    plt.tight_layout()\n    plt.show()\n\n# === RUN VISUALIZATION ===\nvisualize_lighting_steps()\n\n# === LOAD DATA ===\nX, y = load_dataset(INPUT_DIR, max_images=MAX_IMAGES, training=True)\ny_cat = to_categorical(y, num_classes=NUM_CLASSES)\n\n# === SPLIT ===\nX_train, X_test, y_train, y_test = train_test_split(\n    X, y_cat, test_size=0.2, stratify=y, random_state=42\n)\n\n# === DATA GENERATORS ===\ntrain_gen = ImageDataGenerator(\n    rotation_range=15,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    zoom_range=0.1,\n    horizontal_flip=True\n).flow(X_train, y_train, batch_size=32, shuffle=True)\n\nval_gen = ImageDataGenerator().flow(X_test, y_test, batch_size=32, shuffle=False)\n\n# === MODEL ===\nmodel = tf.keras.Sequential([\n    tf.keras.layers.Input(shape=(224, 224, 3)),\n    tf.keras.layers.Conv2D(32, (3, 3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(),\n    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(),\n    tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),\n    tf.keras.layers.GlobalAveragePooling2D(),\n    tf.keras.layers.Dense(128, activation='relu'),\n    tf.keras.layers.Dense(NUM_CLASSES, activation='softmax')\n])\n\nmodel.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\n\n# === CALLBACKS ===\nreduce_lr = ReduceLROnPlateau(\n    monitor='val_accuracy',\n    factor=0.5,\n    patience=2,\n    verbose=1,\n    min_lr=1e-6\n)\n\n# === TRAIN ===\nprint(\"🚀 Training...\")\nmodel.fit(train_gen, epochs=5, validation_data=val_gen, callbacks=[reduce_lr])\n\n# === EVALUATE ===\nprint(\"\\n📊 Evaluating on test set...\")\nloss, acc = model.evaluate(val_gen)\nprint(f\"✅ Test Accuracy: {acc:.4f}, Test Loss: {loss:.4f}\")\n\n# === SHOW PREDICTIONS ===\nif SHOW_TEST_IMAGES:\n    print(\"\\n🖼️ Showing test predictions...\")\n    X_vis = X_test[:10]\n    y_true = np.argmax(y_test[:10], axis=1)\n    y_pred = np.argmax(model.predict(X_vis), axis=1)\n\n    fig, axes = plt.subplots(2, 5, figsize=(15, 6))\n    for i in range(10):\n        axes[i//5, i%5].imshow(X_vis[i])\n        true_label = LABELS[IDX_TO_LABEL[y_true[i]]]\n        pred_label = LABELS[IDX_TO_LABEL[y_pred[i]]]\n        axes[i//5, i%5].set_title(f\"True: {true_label}\\nPred: {pred_label}\", fontsize=9)\n        axes[i//5, i%5].axis('off')\n    plt.tight_layout()\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-06T19:40:07.747017Z","iopub.execute_input":"2025-08-06T19:40:07.747655Z","iopub.status.idle":"2025-08-06T19:42:02.902620Z","shell.execute_reply.started":"2025-08-06T19:40:07.747631Z","shell.execute_reply":"2025-08-06T19:42:02.901543Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport random\n\n# === CONFIGURATION ===\nINPUT_DIR = \"/kaggle/input/state-farm-distracted-driver-detection/imgs/train\"\nTARGET_SIZE = (224, 224)\nSCALE_TYPE = '[0,1]'  # or '[-1,1]'\nRANDOM_SEED = 42\nrandom.seed(RANDOM_SEED)\n\n# === LABELS ===\nLABELS = {\n    'c0': \"safe_driving\",\n    'c1': \"texting_right\",\n    'c2': \"talking_on_the_phone_right\",\n    'c3': \"texting_left\",\n    'c4': \"talking_on_the_phone_left\",\n    'c5': \"operating_the_radio\",\n    'c6': \"drinking\",\n    'c7': \"reaching_behind\",\n    'c8': \"hair_and_makeup\",\n    'c9': \"talking_to_passenger\"\n}\n\n# === 1. STANDARD IMAGE PROCESSING ===\ndef resize_and_scale(img, target_size=TARGET_SIZE, scale_type=SCALE_TYPE):\n    \"\"\"\n    Resize image with appropriate interpolation and scale pixel values.\n    \"\"\"\n    h, w = img.shape[:2]\n    if h > target_size[0] or w > target_size[1]:\n        img = cv2.resize(img, target_size, interpolation=cv2.INTER_AREA)\n    else:\n        img = cv2.resize(img, target_size, interpolation=cv2.INTER_CUBIC)\n\n    img = img.astype(np.float32)\n\n    if scale_type == '[0,1]':\n        img /= 255.0\n    elif scale_type == '[-1,1]':\n        img = (img / 127.5) - 1.0\n\n    return img\n\n# === 2. LIGHTING CONDITION ENHANCEMENT ===\ndef adaptive_CLAHE(img):\n    \"\"\"Apply adaptive CLAHE to L channel of LAB space.\"\"\"\n    lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)\n    l, a, b = cv2.split(lab)\n\n    brightness = np.mean(l)\n    contrast = np.std(l)\n\n    if brightness < 90 and contrast < 40:\n        clip_limit = 3.0\n    elif contrast > 60:\n        clip_limit = 1.2\n    else:\n        clip_limit = 2.0\n\n    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=(8, 8))\n    l = clahe.apply(l)\n\n    lab = cv2.merge((l, a, b))\n    return cv2.cvtColor(lab, cv2.COLOR_LAB2RGB)\n\ndef adaptive_gamma(img):\n    \"\"\"Adjust gamma based on mean brightness.\"\"\"\n    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n    mean_brightness = np.mean(gray)\n    gamma = np.interp(mean_brightness, [50, 200], [0.6, 1.6])\n    img = np.power(img / 255.0, gamma)\n    return np.clip(img * 255, 0, 255).astype(np.uint8)\n\ndef adaptive_brightness_contrast(img):\n    \"\"\"Normalize brightness and contrast.\"\"\"\n    target_mean, target_std = 128, 64\n    mean, std = cv2.meanStdDev(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY))\n    mean, std = mean[0][0], std[0][0]\n\n    alpha = target_std / (std + 1e-6)\n    beta = target_mean - mean * alpha\n\n    return cv2.convertScaleAbs(img, alpha=alpha, beta=beta)\n\n# === 3. FULL PIPELINE FUNCTION ===\ndef preprocess_pipeline(img, apply_resize=True, apply_scale=True):\n    \"\"\"\n    Apply full preprocessing: resize, scale, CLAHE, gamma correction, brightness/contrast normalization.\n    \"\"\"\n    if apply_resize or apply_scale:\n        img = resize_and_scale(img)\n\n    clahe_img = adaptive_CLAHE(img.astype(np.uint8))\n    clahe_gamma_img = adaptive_gamma(clahe_img)\n    final_img = adaptive_brightness_contrast(clahe_gamma_img)\n\n    return clahe_img, clahe_gamma_img, final_img\n\n# === 4. VISUALIZATION ===\ndef visualize_pipeline_all_classes():\n    sample_images = []\n\n    for cls in LABELS.keys():\n        sample_dir = os.path.join(INPUT_DIR, cls)\n        all_files = os.listdir(sample_dir)\n        if not all_files:\n            continue\n\n        fname = random.choice(all_files)\n        img_path = os.path.join(sample_dir, fname)\n\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n        clahe_img, clahe_gamma_img, final_img = preprocess_pipeline(img, apply_resize=False, apply_scale=False)\n\n        sample_images.append((cls, img, clahe_img, clahe_gamma_img, final_img))\n\n    titles = [\"Original\", \"CLAHE only\", \"CLAHE + Gamma\", \"CLAHE + Gamma + Bright/Contrast\"]\n    fig, axes = plt.subplots(len(sample_images), 4, figsize=(12, len(sample_images) * 3))\n\n    for row_idx, (cls, orig, clahe, gamma, final) in enumerate(sample_images):\n        for col_idx, (title, im) in enumerate(zip(titles, [orig, clahe, gamma, final])):\n            axes[row_idx, col_idx].imshow(im)\n            if row_idx == 0:\n                axes[row_idx, col_idx].set_title(title, fontsize=9)\n            if col_idx == 0:\n                axes[row_idx, col_idx].set_ylabel(LABELS[cls], fontsize=8)\n            axes[row_idx, col_idx].axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n\n# === RUN VISUALIZATION ===\nvisualize_pipeline_all_classes()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-06T20:06:01.371758Z","iopub.execute_input":"2025-08-06T20:06:01.372036Z","iopub.status.idle":"2025-08-06T20:06:04.880605Z","shell.execute_reply.started":"2025-08-06T20:06:01.372015Z","shell.execute_reply":"2025-08-06T20:06:04.879860Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport random\n\n# === CONFIGURATION ===\nINPUT_DIR = \"/kaggle/input/state-farm-distracted-driver-detection/imgs/train\"\nTARGET_SIZE = (224, 224)\nSCALE_TYPE = '[0,1]'  # or '[-1,1]'\nRANDOM_SEED = 42\nrandom.seed(RANDOM_SEED)\n\n# === LABELS ===\nLABELS = {\n    'c0': \"safe_driving\",\n    'c1': \"texting_right\",\n    'c2': \"talking_on_the_phone_right\",\n    'c3': \"texting_left\",\n    'c4': \"talking_on_the_phone_left\",\n    'c5': \"operating_the_radio\",\n    'c6': \"drinking\",\n    'c7': \"reaching_behind\",\n    'c8': \"hair_and_makeup\",\n    'c9': \"talking_to_passenger\"\n}\n\nclass ImagePreprocessor:\n    \"\"\"\n    A class to handle a full image preprocessing pipeline for computer vision tasks.\n    \"\"\"\n    def __init__(self, target_size=TARGET_SIZE, scale_type=SCALE_TYPE):\n        self.target_size = target_size\n        self.scale_type = scale_type\n\n    def resize_and_scale(self, img):\n        \"\"\"\n        Resize image with appropriate interpolation and scale pixel values.\n        \"\"\"\n        h, w = img.shape[:2]\n        if h > self.target_size[0] or w > self.target_size[1]:\n            img = cv2.resize(img, self.target_size, interpolation=cv2.INTER_AREA)\n        else:\n            img = cv2.resize(img, self.target_size, interpolation=cv2.INTER_CUBIC)\n\n        img = img.astype(np.float32)\n\n        if self.scale_type == '[0,1]':\n            img /= 255.0\n        elif self.scale_type == '[-1,1]':\n            img = (img / 127.5) - 1.0\n\n        return img\n\n    def adaptive_CLAHE(self, img):\n        \"\"\"Apply adaptive CLAHE to L channel of LAB space.\"\"\"\n        lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)\n        l, a, b = cv2.split(lab)\n\n        brightness = np.mean(l)\n        contrast = np.std(l)\n\n        if brightness < 90 and contrast < 40:\n            clip_limit = 3.0\n        elif contrast > 60:\n            clip_limit = 1.2\n        else:\n            clip_limit = 2.0\n\n        clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=(8, 8))\n        l = clahe.apply(l)\n\n        lab = cv2.merge((l, a, b))\n        return cv2.cvtColor(lab, cv2.COLOR_LAB2RGB)\n\n    def adaptive_gamma(self, img):\n        \"\"\"Adjust gamma based on mean brightness.\"\"\"\n        gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n        mean_brightness = np.mean(gray)\n        gamma = np.interp(mean_brightness, [50, 200], [0.6, 1.6])\n        img = np.power(img / 255.0, gamma)\n        return np.clip(img * 255, 0, 255).astype(np.uint8)\n\n    def adaptive_brightness_contrast(self, img):\n        \"\"\"Normalize brightness and contrast.\"\"\"\n        target_mean, target_std = 128, 64\n        mean, std = cv2.meanStdDev(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY))\n        mean, std = mean[0][0], std[0][0]\n\n        alpha = target_std / (std + 1e-6)\n        beta = target_mean - mean * alpha\n\n        return cv2.convertScaleAbs(img, alpha=alpha, beta=beta)\n    \n    def apply_pipeline(self, img, apply_resize=True, apply_scale=True):\n        \"\"\"\n        Apply the full preprocessing pipeline to a single image.\n        \"\"\"\n        if apply_resize or apply_scale:\n            img = self.resize_and_scale(img)\n\n        # The lighting enhancement functions should be applied to images with uint8 pixel values\n        clahe_img = self.adaptive_CLAHE(img.astype(np.uint8))\n        clahe_gamma_img = self.adaptive_gamma(clahe_img)\n        final_img = self.adaptive_brightness_contrast(clahe_gamma_img)\n\n        return clahe_img, clahe_gamma_img, final_img\n\n# === 4. VISUALIZATION ===\ndef visualize_pipeline_all_classes():\n    preprocessor = ImagePreprocessor()\n    sample_images = []\n\n    for cls in LABELS.keys():\n        sample_dir = os.path.join(INPUT_DIR, cls)\n        all_files = os.listdir(sample_dir)\n        if not all_files:\n            continue\n\n        fname = random.choice(all_files)\n        img_path = os.path.join(sample_dir, fname)\n\n        img = cv2.imread(img_path)\n        if img is None:\n            continue\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n        # The visualization pipeline will now use the new class method\n        clahe_img, clahe_gamma_img, final_img = preprocessor.apply_pipeline(\n            img, apply_resize=False, apply_scale=False\n        )\n        sample_images.append((cls, img, clahe_img, clahe_gamma_img, final_img))\n\n    titles = [\"Original\", \"CLAHE only\", \"CLAHE + Gamma\", \"CLAHE + Gamma + Bright/Contrast\"]\n    num_classes = len(sample_images)\n    if num_classes == 0:\n        print(\"No images found for visualization.\")\n        return\n\n    fig, axes = plt.subplots(num_classes, 4, figsize=(12, num_classes * 3))\n    \n    # Handle the case where there's only one row (single class)\n    if num_classes == 1:\n        axes = np.expand_dims(axes, axis=0)\n\n    for row_idx, (cls, orig, clahe, gamma, final) in enumerate(sample_images):\n        for col_idx, (title, im) in enumerate(zip(titles, [orig, clahe, gamma, final])):\n            axes[row_idx, col_idx].imshow(im)\n            if row_idx == 0:\n                axes[row_idx, col_idx].set_title(title, fontsize=9)\n            if col_idx == 0:\n                axes[row_idx, col_idx].set_ylabel(LABELS[cls], fontsize=8)\n            axes[row_idx, col_idx].axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n\n# === RUN VISUALIZATION ===\nif __name__ == \"__main__\":\n    visualize_pipeline_all_classes()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-06T20:11:01.374336Z","iopub.execute_input":"2025-08-06T20:11:01.374668Z","iopub.status.idle":"2025-08-06T20:11:07.313306Z","shell.execute_reply.started":"2025-08-06T20:11:01.374642Z","shell.execute_reply":"2025-08-06T20:11:07.312417Z"}},"outputs":[],"execution_count":null}]}