{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "64a8cca0",
   "metadata": {},
   "source": [
    "# Exercise 3: Extracting image features"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4aab8777",
   "metadata": {},
   "source": [
    "## 1. Install dependencies, **if needed**\n",
    "\n",
    "Run the commands below to install the dependencies"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d7349216",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -q pillow matplotlib scikit-image scikit-learn gdown"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39c1b080",
   "metadata": {},
   "source": [
    "## 2. Read image and crop region\n",
    "\n",
    "- Download the image by running the next cell"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5115437a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import zipfile\n",
    "import gdown\n",
    "import os\n",
    "\n",
    "if not os.path.exists(\"image_week3\"):\n",
    "    id = \"1UbOP9u0EfNBhcLA_9rJ7gDoxBQTZzGjX\"\n",
    "    gdown.download(id=id, output=\"image_week3.zip\", quiet=True)\n",
    "    with zipfile.ZipFile(\"image_week3.zip\", 'r') as zip_ref:\n",
    "        zip_ref.extractall()\n",
    "    \n",
    "print(os.listdir(\"image_week3\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d269e797",
   "metadata": {},
   "source": [
    "- Alternatively, download the image from [here](https://drive.google.com/file/d/1UbOP9u0EfNBhcLA_9rJ7gDoxBQTZzGjX/view?usp=sharing). "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0856d456",
   "metadata": {},
   "source": [
    "- The zip file contains one normalized image with 3 bands (near infrared, red and green). Read the image using the function ***imread*** of the ***skimage*** library, and visualize it using ***matplotlib***\n",
    "- Use the provided bounding boxes to crop a region that contains a building\n",
    "- Visualize the patch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "62eebd56",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from skimage.io import imsave, imread\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# TODO: Read image\n",
    "\n",
    "# TODO: Visualize the image using matplotlib\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ebe17e2",
   "metadata": {},
   "outputs": [],
   "source": [
    "y_min = 210\n",
    "x_min = 494\n",
    "y_max = 351\n",
    "x_max = 697\n",
    "# TODO; Crop the image using the bounding box coordinates defined above\n",
    "crop_arr = "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b5930d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO: Display the cropped region \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aebecd5d",
   "metadata": {},
   "source": [
    "## 3. Implement functions to compute image features \n",
    "\n",
    "- Implement 3 functions to compute image features:\n",
    "    - Compute the average value for each of the three bands\n",
    "    - Compute the standard deviation for each of the three bands\n",
    "    - Compute the histogram for each of the three bands"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62e4b6ab",
   "metadata": {},
   "source": [
    "### Computing the average feature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47f2fc6a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def convert_data_to_shape_pixels_by_bands(data):\n",
    "    \"\"\"\n",
    "    If needed it converts data to a matrix with shape: num_pixels x num_features\n",
    "    If data is already a 2D numpy array it returns the same array\n",
    "    Parameters\n",
    "    ==================\n",
    "    data: 2D or 3D numpy array\n",
    "    \n",
    "    Returns\n",
    "    ==================\n",
    "    output: 2D numpy array with shape: num_pixels x num_features\n",
    "    \"\"\"\n",
    "    num_dimensions = len(data.shape)\n",
    "    assert(num_dimensions == 2 or num_dimensions == 3)\n",
    "    if num_dimensions == 3:\n",
    "        num_bands = data.shape[2]\n",
    "        return data.reshape((-1, num_bands))\n",
    "    else:\n",
    "        return data\n",
    "\n",
    "def compute_average_feature_with_loop(data):\n",
    "    # Convert data to the shape (num_pixels x num_bands)\n",
    "    data_2d = convert_data_to_shape_pixels_by_bands(data)\n",
    "    # TODO: Get the number of bands\n",
    "    num_bands = \n",
    "    avg_features = np.zeros(num_bands)\n",
    "    for b in range(num_bands):\n",
    "        # TODO: compute the average value of each band (use the function np.mean)\n",
    "        avg_features[b] = \n",
    "    return avg_features\n",
    "\n",
    "# same function but without a for loop\n",
    "def compute_average_feature(data):\n",
    "    # Convert data to the shape (num_pixels x num_bands)\n",
    "    data_2d = convert_data_to_shape_pixels_by_bands(data)\n",
    "    # TODO: compute the average value of each band using np.mean() directly\n",
    "    avg_features = \n",
    "    return avg_features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b4c1e176",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO: Compute and print the average feature of the cropped region using both functions\n",
    "#       Check that the results are the same\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73b4587c",
   "metadata": {},
   "source": [
    "### Compute the standard deviation feature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7036ae75",
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_standard_deviation_feature(data):\n",
    "    # If needed convert data to the shape (num_pixels x num_bands)\n",
    "    data_2d = convert_data_to_shape_pixels_by_bands(data)\n",
    "    # TODO: Compute the standard deviation feature (using the numpy function np.std)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1cd0aebb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO: Compute and print the standard deviation feature of the cropped region\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccce31c5",
   "metadata": {},
   "source": [
    "### Compute the histogram feature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1101ed9e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_histogram_feature(data, num_bins=10):\n",
    "    # If needed convert data to the shape (num_pixels x num_bands)\n",
    "    data_2d = convert_data_to_shape_pixels_by_bands(data)\n",
    "    num_bands = data_2d.shape[1]\n",
    "    hist_features = np.zeros((num_bands, num_bins)).astype(np.float32)\n",
    "    for b in range(num_bands):\n",
    "        # TODO: compute the histogram for each band \n",
    "        #       use the function np.histogram(array, bins=num_bins)\n",
    "        hist, _ = \n",
    "        hist_features[b, :] = hist\n",
    "    # Return a 1D array containing all the values\n",
    "    return hist_features.flatten()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6685b289",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO: Compute and print the histogram feature of the cropped region\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19d68f66",
   "metadata": {},
   "source": [
    "## 4. Segment the image into regions using SLIC\n",
    "\n",
    "Use the function ***slic*** from the ***skimage*** library to segment the image into regions. You can find more information about how to use this function in the [skimage documentation](https://scikit-image.org/docs/dev/api/skimage.segmentation.html#skimage.segmentation.slic)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3b7194c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO: Segment the image into 700 regions using SLIC (you can also play with different number of regions)\n",
    "\n",
    "segmented_image = \n",
    "\n",
    "# Compute the real number of regions \n",
    "num_regions = len(np.unique(segmented_image))\n",
    "print(num_regions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d7d4bfc8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from skimage.segmentation import mark_boundaries\n",
    "# Plot borders of regions obtained by SLIC\n",
    "plt.figure(figsize = (11,11)) \n",
    "plt.imshow(mark_boundaries(norm_image, segmented_image))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "291dd407",
   "metadata": {},
   "source": [
    "## 5. Compute image features from regions\n",
    "\n",
    "- Implement the function (below) to compute image feature from regions obtained by SLIC\n",
    "- Compute the features for all the regions of the example image\n",
    "- Save compute features on disk using the function ***np.save***\n",
    "\n",
    "Tip: if you encounter **IndexError**, set the ***start_label*** parameter properly when calling ***slic*** in section 4."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f5ebb2c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_image_features_from_regions(image, segmentation_map):\n",
    "    num_regions = len(np.unique(segmentation_map))\n",
    "    all_features = []\n",
    "    for id_region in range(num_regions):\n",
    "        # Obtain pixel values of each regions, with shape (num_pixels x num_bands)\n",
    "        pixel_values = image[segmentation_map==id_region]\n",
    "        # TODO: compute the average, standard deviation and histogram features\n",
    "        #       and concatenated them using the function (np.concatenate)\n",
    "        \n",
    "        \n",
    "        \n",
    "        features = \n",
    "        # Add concatenated features to the variable all_features\n",
    "        all_features.append(features)\n",
    "    # convert list to numpy array of shape: (num_regions x feature_vector_length)\n",
    "    return np.array(all_features).astype(np.float32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "df44f6c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO: Compute features for each region\n",
    "features_per_region = "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "062742e3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO: Save features using the function np.save(output_path, array)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42a33bd1",
   "metadata": {},
   "source": [
    "## 6. Answer the remaining questions of the PDF instructions file"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
