{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "w_RawWFWbwTS" }, "source": [ "# Clasificador básico usando HOG" ] }, { "cell_type": "markdown", "metadata": { "id": "ntY-b82LbwTV" }, "source": [ "## Imports" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "cKlzWR23bwTW" }, "outputs": [], "source": [ "# Encadenar iterables\n", "from itertools import chain\n", "\n", "# Proporciona una barra de progreso rápida\n", "from tqdm import tqdm\n", "\n", "# Selección aleatoria de una lista sin repetición\n", "from random import sample\n", "\n", "# Interfaz para hacer gráficos y visualizaciones\n", "import matplotlib.pyplot as plt\n", "\n", "# Computación científica\n", "import numpy as np\n", "\n", "# Extraer parches (pequeños subconjuntos de imágenes) de imágenes\n", "from sklearn.feature_extraction.image import PatchExtractor\n", "\n", "# data: conjunto de datos de muestra y funciones de carga\n", "# color: convertir imágenes entre espacios de color\n", "# feature: funciones para identificar y extraer características de imágenes\n", "from skimage import data, color, feature\n", "\n", "# Cambiar el tamaño de una imagen\n", "from skimage.transform import resize, rescale\n", "\n", "# Descarga y carga en memoria un conjunto de datos de imágenes de caras de personas famosas\n", "from sklearn.datasets import fetch_lfw_people\n", "\n", "# Regresión logística\n", "from sklearn.linear_model import LogisticRegression\n", "\n", "# Exactitud en validación cruzada\n", "from sklearn.model_selection import cross_val_score\n", "\n", "# Divide los datos en conjuntos de entrenamiento y prueba\n", "from sklearn.model_selection import train_test_split\n", "\n", "# Genera un informe detallado de métricas de clasificación\n", "from sklearn.metrics import classification_report\n", "\n", "# Funciones para trabajar con la curva ROC y calcular el área bajo la curva ROC\n", "from sklearn.metrics import roc_curve, roc_auc_score\n", "\n", "# Genera puntos para visualizar una curva de aprendizaje\n", "from sklearn.model_selection import learning_curve" ] }, { "cell_type": "markdown", "metadata": { "id": "yAQHcl4ubwTX" }, "source": [ "## Histogram of oriented gradients (HOG)" ] }, { "cell_type": "markdown", "metadata": { "id": "O6KWFTlcbwTX" }, "source": [ "### Ejemplo de uso" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "BHnbkrT2bwTX" }, "outputs": [], "source": [ "# Imagen de ejemplo\n", "data.chelsea().shape" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "-4tARppsbwTY" }, "outputs": [], "source": [ "# Trabajaremos en escala de grises\n", "chelsea_gray = color.rgb2gray(data.chelsea())\n", "print(chelsea_gray.shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "x9gB-a2RbwTY" }, "outputs": [], "source": [ "# Extraemos las HOG features\n", "hog_features, hog_vis = feature.hog(chelsea_gray, visualize=True, feature_vector=False)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "wcreUjVsbwTZ" }, "outputs": [], "source": [ "# Analizamos el output\n", "print('Type de hog_vec: ',type(hog_features))\n", "print('Shape de hog_vec: ',hog_features.shape)\n", "print('Type de hog_vis: ',type(hog_vis))\n", "print('Shape de hog_vis: ',hog_vis.shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Ix_UZWSSbwTZ" }, "outputs": [], "source": [ "# Visualización\n", "fig, ax = plt.subplots(1, 2, figsize=(8, 4))\n", "ax[0].imshow(chelsea_gray, cmap='gray')\n", "ax[0].set_title('Imagen de entrada')\n", "\n", "ax[1].imshow(hog_vis, cmap='gray')\n", "ax[1].set_title('Visualización de las HOG features');" ] }, { "cell_type": "markdown", "metadata": { "id": "JQm0uh3GbwTZ" }, "source": [ "### Explicación" ] }, { "cell_type": "markdown", "metadata": { "id": "dv08dsvsbwTZ" }, "source": [ "Las características HOG (Histogram of Oriented Gradients) son utilizadas principalmente en computer vision y en procesamiento de imágenes para la detección de objetos.\n", "\n", "1. **Gradiente de la Imagen**:\n", " \n", " El primer paso para calcular las características HOG es determinar los gradientes en $x$ e $y$ para la imagen.\n", " \n", " **Ejemplo de cálculo**\n", " \n", " Imaginemos que tenemos una pequeña porción de una imagen (3x3):\n", " $$I = \\begin{bmatrix} 10 & 20 & 30 \\\\ 40 & 50 & 60 \\\\ 70 & 80 & 90 \\end{bmatrix}$$\n", " Para calcular el gradiente en el centro de la imagen y en la dirección $x$, $I_x$, hacemos:\n", " $$I_x = 60-40=20$$\n", " Para calcular el gradiente en el centro de la imagen y en la dirección $y$, $I_y$, hacemos:\n", " $$I_y = 20-80=-60$$\n", " \n", " Este cálculo se realiza para cada posición de la imagen (ajustando los bordes según sea necesario, padding) para obtener dos imágenes completas: una para $I_x$ y otra para $I_y$, que representan los gradientes en las direcciones $x$ e $y$, respectivamente.\n", "\n", "2. **Magnitud y Dirección del Gradiente**:\n", "\n", " Una vez que tenemos los gradientes en $x$ e $y$, podemos calcular la magnitud y la dirección del gradiente en cada píxel:\n", "\n", " $$\\text{Magnitud} = \\sqrt{I_x^2 + I_y^2}\\quad \\text{Dirección} = \\arctan(I_y, I_x)$$\n", "\n", " Aquí, $\\arctan$ devuelve la dirección en grados entre $[0, 180]$.\n", "\n", "3. **Creación de Celdas y Binning de Gradients**:\n", "\n", " Se divide la imagen en pequeñas celdas (por ejemplo, 8x8 píxeles). Para cada celda, se crea un histograma de gradientes orientados. Esto se hace dividiendo el rango de direcciones de los gradientes, 0 a 180 grados, en \"bins\". Por cada píxel en la celda, se añade la magnitud de su gradiente al bin correspondiente a su dirección.\n", "\n", "4. **Normalización**:\n", "\n", " Las celdas se agrupan en bloques (por ejemplo, 2x2 celdas). Se normaliza el histograma de cada bloque para reducir el efecto de cambios de iluminación. Una técnica común es usar la normalización L2:\n", "\n", " $$h_{\\text{normalizado}} = \\frac{h}{\\sqrt{\\|h\\|_2^2 + \\epsilon^2}}$$\n", "\n", " Donde $h$ es el histograma del bloque y $\\epsilon$ es una pequeña constante para evitar la división por cero.\n", "\n", "5. **Concatenación**:\n", "\n", " Finalmente, los histogramas normalizados de todos los bloques se concatenan para formar el descriptor HOG de la imagen." ] }, { "cell_type": "markdown", "metadata": { "id": "aPKBfgPubwTa" }, "source": [ "La función `skimage.feature.hog` es una implementación de las características HOG en la biblioteca `skimage`:\n", "\n", "1. **image**:\n", " La imagen de entrada sobre la que se calcularán las características HOG.\n", "\n", "2. **orientations** (default=9):\n", " Número de bins de orientación. Se refiere al número de divisiones en el histograma de gradientes orientados. Por defecto, se dividen los 180 grados en 9 bins, resultando en bins de 20 grados cada uno.\n", "\n", "3. **pixels_per_cell** (default=(8, 8)):\n", " Tamaño de la celda en píxeles (alto, ancho). En el contexto de la explicación previa, se mencionó la creación de celdas (por ejemplo, 8x8 píxeles) y la construcción de un histograma de gradientes orientados para cada celda.\n", "\n", "4. **cells_per_block** (default=(3, 3)):\n", " Tamaño del bloque en celdas. Un bloque consiste en varias celdas y se utiliza para la normalización. En el ejemplo anterior, se mencionó que las celdas se agrupan en bloques (por ejemplo, 2x2 celdas) para la normalización. Aquí, el valor predeterminado sería un bloque de 3x3 celdas.\n", "\n", "5. **block_norm** (default='L2-Hys'):\n", " Método para normalizar los histogramas de gradientes en los bloques. 'L2-Hys' es la normalización L2 seguida de un recorte (limitando valores máximos) y luego una nueva normalización L2. Esta técnica es común en el cálculo de características HOG y ayuda a mejorar el rendimiento del descriptor.\n", "\n", "6. **visualize** (default=False):\n", " Si es True, también devuelve una imagen que visualiza las características HOG. Útil para entender y visualizar lo que está capturando el descriptor HOG de una imagen en particular.\n", "\n", "7. **transform_sqrt** (default=False):\n", " Si es True, aplica una compresión de rango de valores mediante la raíz cuadrada antes del cálculo de gradientes. Esto puede ayudar a reducir el efecto de sombras o iluminación fuerte en la imagen.\n", "\n", "8. **feature_vector** (default=True):\n", " Si es True, devuelve las características como un vector unidimensional. De lo contrario, las características se devuelven en la estructura de celdas/bloques original.\n", "\n", "9. **channel_axis** (opcional):\n", " Especifica el eje de color en el caso de una imagen multicanal (por ejemplo, RGB). Las características HOG se calcularán por separado para cada canal y luego se concatenarán.\n", "\n", "El `output` es un array de dimensiones\n", "\n", "**(n_blocks_row, n_blocks_col, n_cells_row, n_cells_col, n_orient)**\n", "\n", "o un vector de dimensión el producto de dichas dimensiones." ] }, { "cell_type": "markdown", "metadata": { "id": "Pwk_fylObwTa" }, "source": [ "### Explicación detallada de la normalización" ] }, { "cell_type": "markdown", "metadata": { "id": "lWUK81fYbwTa" }, "source": [ "La normalización se utiliza para reducir el impacto de las variaciones en la iluminación y el contraste en las imágenes.\n", "\n", "La normalización suele llevarse a cabo en bloques, que son grupos de celdas. Por ejemplo, si tenemos celdas de $8 \\times 8$ píxeles y definimos bloques de $2 \\times 2$ celdas, tendríamos bloques de $16 \\times 16$ píxeles.\n", "\n", "**Proceso de Normalización:**\n", "\n", "1. Calcular el histograma de gradientes orientados para cada celda en el bloque.\n", "2. Concatenar los histogramas de todas las celdas en el bloque para formar un vector.\n", "3. Normalizar este vector.\n", "\n", "Uno de los métodos más comunes para normalizar es la normalización L2. Si tenemos un vector $v$, la norma L2 se calcula como:\n", "\n", "$$\\|v\\|_2 = \\sqrt{v_1^2 + v_2^2 + \\ldots + v_n^2}$$\n", "\n", "Y el vector normalizado $v'$ se obtiene dividiendo cada componente del vector $v$ por $\\|v\\|_2$.\n", "\n", "**Ejemplo Numérico:**\n", "\n", "Supongamos que tenemos un bloque de $2 \\times 2$ celdas y cada celda tiene un histograma de gradientes orientados con 2 bins.\n", "\n", "Histogramas de las celdas:\n", "\n", "$$Celda_1 = [2, 3]$$\n", "$$Celda_2 = [1, 4]$$\n", "$$Celda_3 = [3, 1]$$\n", "$$Celda_4 = [2, 2]$$\n", "\n", "Concatenamos estos histogramas para formar un vector para el bloque:\n", "\n", "$$v = [2, 3, 1, 4, 3, 1, 2, 2]$$\n", "\n", "Calculamos la norma L2 de $v$:\n", "\n", "$$\\|v\\|_2 = \\sqrt{2^2 + 3^2 + 1^2 + 4^2 + 3^2 + 1^2 + 2^2 + 2^2}$$\n", "$$\\|v\\|_2 = \\sqrt{4 + 9 + 1 + 16 + 9 + 1 + 4 + 4}$$\n", "$$\\|v\\|_2 = \\sqrt{48}$$\n", "$$\\|v\\|_2 = 6.93$$\n", "\n", "Finalmente, normalizamos $v$:\n", "\n", "$$v' = \\frac{v}{\\|v\\|_2} = \\left[\\frac{2}{6.93}, \\frac{3}{6.93}, \\ldots\\right]$$\n", "\n", "El vector $v'$ es el vector de características HOG normalizado para ese bloque." ] }, { "cell_type": "markdown", "metadata": { "id": "Z5wxmu2lbwTa" }, "source": [ "### Explicación de la visualización" ] }, { "cell_type": "markdown", "metadata": { "id": "aNfqVWkzbwTa" }, "source": [ "La visualización de las características HOG es útil para comprender cómo estas características representan la estructura y orientación de los bordes en una imagen. La visualización HOG se basa en los histogramas de gradientes orientados que se calculan para cada celda en la imagen.\n", "\n", "Para visualizar las características HOG:\n", "\n", "1. **Cada celda se representa con un histograma**:\n", " En la mayoría de las implementaciones, este histograma tiene un número predefinido de \"bins\" que representan rangos de orientaciones. Por ejemplo, si usamos 9 bins, cada bin cubre un rango de 20° (es decir, 0-20°, 20-40°, etc., hasta 180°).\n", "\n", "2. **Cada bin del histograma se representa mediante una línea (o flecha) cuya orientación corresponde al rango del bin (u ortogonal al rango del bin)**:\n", " La longitud (o magnitud) de esta línea es proporcional a la cantidad acumulada en ese bin del histograma. Una mayor magnitud implica que hay muchos gradientes (o bordes) en la imagen que tienen esa orientación particular en la celda actual.\n", " \n", "3. **Las líneas se dibujan en el centro de la celda correspondiente**:\n", " De esta manera, obtenemos una representación visual donde las zonas con líneas más largas y densas corresponden a áreas de la imagen con estructuras (o bordes) más definidas." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "nuVkTM_ubwTa" }, "outputs": [], "source": [ "# Visualización (zoom)\n", "fig, ax = plt.subplots(1, 2, figsize=(8, 4))\n", "ax[0].imshow(chelsea_gray[75:150,120:220], cmap='gray')\n", "ax[0].set_title('Imagen de entrada')\n", "\n", "ax[1].imshow(hog_vis[75:150,120:220], cmap='gray')\n", "ax[1].set_title('Visualización de las HOG features');" ] }, { "cell_type": "markdown", "metadata": { "id": "yNFDdJKlbwTb" }, "source": [ "La visualización HOG y cómo se relaciona con la imagen original.\n", "\n", "1. **Imagen de Entrada**:\n", " La imagen de la izquierda muestra un ojo en escala de grises. Se pueden identificar estructuras visibles, como el borde del párpado superior e inferior, el iris y la pupila. Estas estructuras tienen bordes bien definidos que serán capturados por las características HOG.\n", "\n", "2. **Visualización de las HOG features**:\n", " La imagen de la derecha es una representación visual de las características HOG calculadas para la imagen de entrada.\n", " \n", " Cada pequeña línea o flecha representa la orientación (u ortogonal) y magnitud del gradiente en una celda particular. Las áreas con estructuras más definidas en la imagen original, como el borde del párpado y el contorno del iris, muestran líneas más prominentes en la visualización HOG. Estas líneas indican la dirección del gradiente y su longitud indica la fuerza del borde o gradiente en esa celda. Las zonas más oscuras o uniformes de la imagen original (como la esclerótica del ojo) tienen menos flechas prominentes en la visualización HOG porque hay menos variación y, por lo tanto, gradientes más débiles.\n", " \n", " La disposición circular del iris y la pupila en la imagen original se refleja en la visualización HOG como un patrón de líneas que sugiere un cambio circular en las orientaciones." ] }, { "cell_type": "markdown", "metadata": { "id": "rjQ_RvembwTb" }, "source": [ "## Dataset de rostros (LFW)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "sdnRX6uibwTb" }, "outputs": [], "source": [ "# Cargamos el dataset\n", "faces = fetch_lfw_people()\n", "positive_patches = faces.images\n", "positive_patches.shape" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "WxnodUCdbwTb" }, "outputs": [], "source": [ "# Vemos que contiene el dataset\n", "faces.keys()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "pAlH7mb8bwTb" }, "outputs": [], "source": [ "print(faces.DESCR)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "TEuXBcRSbwTb" }, "outputs": [], "source": [ "# Muestra de rostros\n", "P = positive_patches.shape[0]\n", "K=16\n", "indices = sample(range(P),k=K)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "j76p9KDabwTc" }, "outputs": [], "source": [ "# Visualización\n", "fig, ax = plt.subplots(4, 4, figsize=(7, 5), subplot_kw=dict(xticks=[], yticks=[]))\n", "axes = ax.ravel()\n", "\n", "for i in range(K):\n", " idx = indices[i]\n", " image = resize(positive_patches[idx],(62,47))\n", " axes[i].imshow(image, cmap='gray')\n", " axes[i].set_title(faces.target_names[faces.target[idx]])\n", "\n", "plt.tight_layout()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "wudZ348cbwTc" }, "outputs": [], "source": [ "# Visualización de las HOG\n", "fig, ax = plt.subplots(4, 4, figsize=(7, 5), subplot_kw=dict(xticks=[], yticks=[]))\n", "axes = ax.ravel()\n", "\n", "for i in range(K):\n", " idx = indices[i]\n", " image = positive_patches[idx]\n", " _, hog_vis = feature.hog(image, visualize=True, feature_vector=False)\n", " axes[i].imshow(hog_vis)\n", " axes[i].set_title(faces.target_names[faces.target[idx]])\n", "\n", "plt.tight_layout()" ] }, { "cell_type": "markdown", "metadata": { "id": "JQ-k3F0gbwTc" }, "source": [ "## Dataset de fondos" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "y8KTpOcGbwTc" }, "outputs": [], "source": [ "# Tomamos algunas imágenes de sklearn\n", "imgs = ['camera',\n", " 'text',\n", " 'coins',\n", " 'moon',\n", " 'page',\n", " 'clock',\n", " 'immunohistochemistry',\n", " 'chelsea',\n", " 'coffee',\n", " 'hubble_deep_field'\n", " ]\n", "\n", "images = []\n", "for name in imgs:\n", " img = getattr(data, name)()\n", " if len(img.shape) == 3 and img.shape[2] == 3: # Chequeamos si la imagen es RGB\n", " img = color.rgb2gray(img)\n", " images.append(img)\n", "\n", "# Imagenes caseras adicionales\n", "for i in range(16):\n", " filename = str(i)+'.jpg'\n", " img = plt.imread(filename)\n", " img = color.rgb2gray(img)\n", " images.append(img)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "BWcfnamSbwTc" }, "outputs": [], "source": [ "# Visualización\n", "fig, ax = plt.subplots(2, 13, figsize=(20, 5), subplot_kw=dict(xticks=[], yticks=[]))\n", "axes = ax.ravel()\n", "\n", "for i in range(len(images)):\n", " image = images[i]\n", " axes[i].imshow(image, cmap='gray')\n", "\n", "plt.tight_layout()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "HTY4cAE1bwTc" }, "outputs": [], "source": [ "# Tamaño de las imágenes de rostros\n", "size = positive_patches[0].shape" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "BibtUBc0bwTc" }, "outputs": [], "source": [ "# Función para extraer porciones de una imagen\n", "def extract_patches(img, N, scale=1.0, patch_size=size):\n", " # Calcula el tamaño del parche extraído basado en el factor de escala dado\n", " extracted_patch_size = tuple((scale * np.array(patch_size)).astype(int))\n", "\n", " # Inicializa un objeto PatchExtractor con el tamaño de parche calculado,\n", " # el número máximo de parches, y una semilla de estado aleatorio\n", " extractor = PatchExtractor(patch_size=extracted_patch_size, max_patches=N, random_state=0)\n", "\n", " # Extrae parches de la imagen dada\n", " # img[np.newaxis] se utiliza la entrada de PatchExtractor es un conjunto de imágenes\n", " patches = extractor.transform(img[np.newaxis])\n", "\n", " # Si el factor de escala no es 1, redimensiona cada parche extraído\n", " # al tamaño del parche original\n", " if scale != 1:\n", " patches = np.array([resize(patch, patch_size) for patch in patches])\n", "\n", " # Devuelve la lista de parches extraídos (y posiblemente redimensionados)\n", " return patches" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "UH71arZxbwTc" }, "outputs": [], "source": [ "# Extraemos las imágenes de fondo\n", "negative_patches = np.vstack([extract_patches(im, 250, scale) for im in tqdm(images, desc='Procesando imágenes') for scale in [0.5, 0.75, 1.0, 1.5, 2.0]])\n", "negative_patches.shape" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "wal2d9ZfbwTc" }, "outputs": [], "source": [ "# Visualizamos una muestra\n", "fig, ax = plt.subplots(6, 10)\n", "for i, axi in enumerate(ax.flat):\n", " axi.imshow(negative_patches[500 * i], cmap='gray')\n", " axi.axis('off')" ] }, { "cell_type": "markdown", "metadata": { "id": "yM9zXzBBbwTd" }, "source": [ "## Armamos los datos: matriz de features y vector de etiquetas" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "XfPSOy2VbwTd" }, "outputs": [], "source": [ "X_train = np.array([feature.hog(im) for im in tqdm(chain(positive_patches, negative_patches), desc='Construyendo X')])\n", "y_train = np.zeros(X_train.shape[0])\n", "y_train[:positive_patches.shape[0]] = 1" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "2P3T7X_cbwTd" }, "outputs": [], "source": [ "X_train.shape" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "J6Rpz2B1bwTd" }, "outputs": [], "source": [ "y_train.shape" ] }, { "cell_type": "markdown", "metadata": { "id": "moKCGK8lbwTd" }, "source": [ "## Ejercicio: Clasificador básico de detección de rostros" ] }, { "cell_type": "markdown", "metadata": { "id": "_-jK2LzKbwTd" }, "source": [ "El objetivo de este ejercicio es construir un detector de rostros utilizando Regresión logística. Deberás completar los espacios marcados con `___` para finalizar el código." ] }, { "cell_type": "markdown", "metadata": { "id": "J08fmNcGbwTh" }, "source": [ "\n", "1. Utiliza la función `cross_val_score` para obtener la exactitud de validación cruzada de un modelo de Regresión logística." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "K0vbPbhcbwTh" }, "outputs": [], "source": [ "cv_acc = cross_val_score(___, verbose=2)\n", "print('Exactitud CV: ',cv_acc)\n", "print('Exactitud promedio: ',___)" ] }, { "cell_type": "markdown", "metadata": { "id": "WJtzbfLBbwTh" }, "source": [ "2. Visualiza la curva de aprendizaje del modelo con la función `learning_curve`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "aLDTPCUlbwTh" }, "outputs": [], "source": [ "clf = ___\n", "train_sizes, train_scores, test_scores = ___\n", "\n", "plt.figure()\n", "plt.plot(train_sizes, train_scores.mean(axis=1), 'o-', label=\"Training score\")\n", "plt.plot(train_sizes, test_scores.mean(axis=1), 'o-', label=\"Cross-validation score\")\n", "plt.xlabel(\"Training examples\")\n", "plt.ylabel(\"Score\")\n", "plt.title(\"Learning Curve\")\n", "plt.legend(loc=\"best\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "Gu8OKkXPbwTh" }, "source": [ "3. Implementa un hold-out para evaluar la Regresión logística." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "OfrMTGHUbwTh" }, "outputs": [], "source": [ "# Dividir los datos en conjuntos de entrenamiento y prueba:\n", "X_train_split, X_test_split, y_train_split, y_test_split = ___\n", "\n", "# Entrenar Regresión Logística\n", "clf = ___\n", "clf.___\n", "\n", "# Predecir las etiquetas y las probabilidades en el conjunto de prueba:\n", "y_pred = clf.___\n", "y_pred_prob = clf.___\n", "\n", "# Generar un informe detallado de métricas de clasificación:\n", "print(___)" ] }, { "cell_type": "markdown", "metadata": { "id": "N1pV5j82bwTi" }, "source": [ "4. Grafica la curva ROC y calcula el AUC." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "tKZQUSVcbwTi" }, "outputs": [], "source": [ "fpr, tpr, thresholds = ___\n", "auc_score = ___\n", "print(f\"AUC Score: {auc_score}\")\n", "\n", "# Graficar la curva ROC\n", "plt.figure()\n", "plt.plot(fpr, tpr, label=f'ROC curve (area = {auc_score:.2f})')\n", "plt.xlabel('False Positive Rate')\n", "plt.ylabel('True Positive Rate')\n", "plt.title('Receiver Operating Characteristic (ROC)')\n", "plt.legend(loc=\"lower right\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "pMibREo0bwTi" }, "source": [ "### Test en una imagen entera" ] }, { "cell_type": "markdown", "metadata": { "id": "MRZJW1k4bwTi" }, "source": [ "5. Entrena un modelo de Regresión Logística con todos los datos de entrenamiento." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "zLE3PeHybwTi" }, "outputs": [], "source": [ "model = ___\n", "model.___" ] }, { "cell_type": "markdown", "metadata": { "id": "gMl4iT37bwTi" }, "source": [ "#### ¿Cómo guardar el modelo y volver a cargarlo?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "HuPgFT4SbwTi" }, "outputs": [], "source": [ "# Para guardar y cargar modelos\n", "from joblib import dump, load" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "RI2yT8PRbwTi" }, "outputs": [], "source": [ "# Suponiendo un modelo entrenado y que se llama 'model'\n", "# Usar 'dump' para guardar el modelo en un archivo\n", "# El primer argumento es el modelo a guardar y el segundo argumento es el nombre del archivo\n", "dump(model, 'modelo.joblib')\n", "\n", "# Para cargar el modelo en el futuro usar la función 'load'\n", "# Se proporciona el nombre del archivo como argumento\n", "model_saved = load('modelo.joblib')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "94yZ2dRObwTi" }, "outputs": [], "source": [ "# Imagen de prueba\n", "test_image = data.astronaut()\n", "test_image = color.rgb2gray(test_image)\n", "test_image = rescale(test_image, 0.5)\n", "test_image = test_image[:160, 40:180]\n", "\n", "plt.imshow(test_image, cmap='gray')\n", "plt.axis('off')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "rF7qRYCLbwTj" }, "source": [ "#### Ejercicio guiado: Construcción de una función de ventana deslizante" ] }, { "cell_type": "markdown", "metadata": { "id": "aznad_iObwTj" }, "source": [ "##### Objetivo:\n", "Crear una función llamada `sliding_window` que tome una imagen y extraiga parches (subimágenes) de ella utilizando una ventana deslizante." ] }, { "cell_type": "markdown", "metadata": { "id": "K4odP1-XbwTj" }, "source": [ "\n", "##### Pasos:" ] }, { "cell_type": "markdown", "metadata": { "id": "sMGgRVgTbwTj" }, "source": [ "1. **Definición de la función**:\n", " - Inicia la función `sliding_window` con los siguientes argumentos:\n", " - `img`: La imagen sobre la que se aplica la ventana deslizante.\n", " - `patch_size`: El tamaño de los parches extraídos.\n", " - `istep`: Paso de desplazamiento en la dirección vertical.\n", " - `jstep`: Paso de desplazamiento en la dirección horizontal.\n", " - `scale`: Factor de escala para ajustar el tamaño del parche.\n", "\n", "3. **Configuración de las dimensiones del parche**:\n", " - Calcula las dimensiones `Ni` y `Nj` del parche ajustadas por el factor de escala.\n", "\n", "4. **Recorrido de la imagen**:\n", " - Utiliza un bucle para recorrer la imagen en las direcciones vertical y horizontal.\n", " - En cada paso, extrae un parche de la imagen.\n", "\n", "5. **Redimensionamiento del parche**:\n", " - Si el factor de escala es diferente de 1, redimensiona el parche al tamaño original del parche.\n", "\n", "6. **Uso del generador**:\n", " - Usa `yield` para devolver las coordenadas actuales y el parche.\n", "\n", "7. **Prueba de la función**:\n", " - Aplica la función `sliding_window` a una imagen de prueba.\n", " - Descompone las tuplas generadas en índices y parches.\n", " - Calcula las características HOG para cada parche y las almacena en un array." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Q8ZlYsYrbwTj" }, "outputs": [], "source": [ "# Define una función para realizar una ventana deslizante (sliding window) sobre una imagen.\n", "def sliding_window(img,\n", " patch_size=positive_patches[0].shape, # Define el tamaño del parche (patch) basado en el primer parche positivo por defecto\n", " istep=2, # Paso de desplazamiento en la dirección i (verticalmente)\n", " jstep=2, # Paso de desplazamiento en la dirección j (horizontalmente)\n", " scale=1.0): # Factor de escala para ajustar el tamaño del parche\n", "\n", " # Calcula las dimensiones Ni y Nj del parche ajustadas por el factor de escala.\n", " Ni, Nj = ___\n", "\n", " # Itera a lo largo de la imagen en la dirección i\n", " for i in range(0, img.shape[0] - Ni, istep):\n", " # Itera a lo largo de la imagen en la dirección j\n", " for j in range(0, img.shape[1] - Ni, jstep):\n", "\n", " # Extrae el parche de la imagen usando las coordenadas actuales i, j.\n", " patch = ___\n", "\n", " # Si el factor de escala es diferente de 1, redimensiona el parche al tamaño original del parche.\n", " if scale != 1:\n", " patch = ___\n", "\n", " # Usa yield para devolver las coordenadas actuales y el parche.\n", " # Esto convierte la función en un generador.\n", " yield ___\n", "\n", "# Utiliza la función de ventana deslizante en una imagen de prueba.\n", "# zip(*...) toma las tuplas generadas y las descompone en índices y parches.\n", "indices, patches = zip(*___)\n", "\n", "# Calcula las características HOG para cada parche y las almacena en un array.\n", "patches_hog = ___\n", "\n", "# Muestra la forma del array de características HOG.\n", "patches_hog.___" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "StXO09LwbwTj" }, "outputs": [], "source": [ "# Predicción en los parches extraídos\n", "labels = model_saved.___\n", "labels.sum()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Xmf3U19vbwTj" }, "outputs": [], "source": [ "# Visualizamos las detecciones\n", "fig, ax = plt.subplots()\n", "ax.imshow(test_image, cmap='gray')\n", "ax.axis('off')\n", "\n", "Ni, Nj = positive_patches[0].shape\n", "indices = np.array(indices)\n", "\n", "for i, j in indices[labels == 1]:\n", " ax.add_patch(plt.Rectangle((j, i), Nj, Ni, edgecolor='red',\n", " alpha=0.3, lw=2, facecolor='none'))" ] }, { "cell_type": "markdown", "metadata": { "id": "Z0BuvCG9bwTk" }, "source": [ "#### Ejercicio guiado: Implementación de Supresión No Máxima (Non-Max Suppression)\n", "\n", "##### Objetivo:\n", "Construir una función llamada `non_max_suppression` que tome un conjunto de índices que representen rectángulos y reduzca el número de rectángulos superpuestos, dejando solo el rectángulo más representativo en áreas superpuestas.\n", "\n", "##### Pasos:\n", "\n", "1. **Definición inicial de la función**:\n", " - Comienza la función `non_max_suppression` con los siguientes argumentos:\n", " - `indices`: Coordenadas de los cuadros.\n", " - `Ni` y `Nj`: Dimensiones de los cuadros.\n", " - `overlapThresh`: Umbral de superposición.\n", "\n", "3. **Verificación de índices**:\n", " - Si no hay rectángulos (índices), la función debe devolver una lista vacía.\n", " - Si las cajas están en formato entero, conviértelas a formato flotante.\n", "\n", "4. **Extracción de coordenadas**:\n", " - Extrae las coordenadas de inicio (`x1`, `y1`) y fin (`x2`, `y2`) de los cuadros. Recuerda añadir las dimensiones `Ni` y `Nj` donde corresponda.\n", "\n", "5. **Cálculo de áreas y ordenación**:\n", " - Calcula el área de cada cuadro y ordena los cuadros según alguna coordenada (por ejemplo, `y2`).\n", "\n", "6. **Loop de supresión**:\n", " - Mientras todavía haya índices en la lista de índices, sigue los siguientes pasos:\n", " - Toma el último índice y agrégalo a la lista de seleccionados.\n", " - Encuentra las coordenadas (x, y) más grandes para el inicio de la caja y las coordenadas (x, y) más pequeñas para el final de la caja.\n", " - Calcula el ancho y alto de la caja.\n", " - Calcula la proporción de superposición.\n", " - Elimina los índices que tengan una proporción de superposición mayor que el umbral.\n", "\n", "7. **Finalización**:\n", " - La función debe devolver solo las cajas (índices) seleccionadas." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "UYHd-S5PbwTk" }, "outputs": [], "source": [ "def non_max_suppression(indices, Ni, Nj, overlapThresh):\n", " # Si no hay rectángulos, regresar una lista vacía\n", " if len(indices) == 0:\n", " return ___\n", "\n", " # Si las cajas son enteros, convertir a flotantes\n", " if indices.dtype.kind == \"i\":\n", " indices = ___\n", "\n", " # Inicializar la lista de índices seleccionados\n", " pick = ___\n", "\n", " # Tomar las coordenadas de los cuadros\n", " x1 = ___\n", " y1 = ___\n", " x2 = ___\n", " y2 = ___\n", "\n", " # Calcula el área de los cuadros y ordena los cuadros\n", " area = ___\n", " idxs = ___\n", "\n", " # Mientras todavía hay índices en la lista de índices\n", " while len(idxs) > 0:\n", " # Toma el último índice de la lista y agrega el índice a la lista de seleccionados\n", " last = ___\n", " i = idxs[last]\n", " pick.append(i)\n", "\n", " # Encontrar las coordenadas (x, y) más grandes para el inicio de la caja y las coordenadas (x, y) más pequeñas para el final de la caja\n", " xx1 = ___\n", " yy1 = ___\n", " xx2 = ___\n", " yy2 = ___\n", "\n", " # Calcula el ancho y alto de la caja\n", " w = ___\n", " h = ___\n", "\n", " # Calcula la proporción de superposición\n", " overlap = (w * h) / area[idxs[:last]]\n", "\n", " # Elimina todos los índices del índice de lista que tienen una proporción de superposición mayor que el umbral proporcionado\n", " idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlapThresh)[0])))\n", "\n", " # Devuelve solo las cajas seleccionadas\n", " return ___.astype(\"int\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JqLz6Q6UbwTk" }, "outputs": [], "source": [ "Ni, Nj = positive_patches[0].shape\n", "detecciones = indices[labels == 1]\n", "detecciones = non_max_suppression(np.array(detecciones),Ni,Nj, 0.3)\n", "\n", "# Visualizamos las detecciones\n", "fig, ax = plt.subplots()\n", "ax.imshow(test_image, cmap='gray')\n", "ax.axis('off')\n", "\n", "for i, j in detecciones:\n", " ax.add_patch(plt.Rectangle((j, i), Nj, Ni, edgecolor='red',\n", " alpha=0.3, lw=2, facecolor='none'))" ] } ], "metadata": { "kernelspec": { "display_name": "base", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.15" }, "orig_nbformat": 4, "colab": { "provenance": [] } }, "nbformat": 4, "nbformat_minor": 0 }