{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# image processing imports\n",
    "from ipfml.processing.segmentation import divide_in_blocks\n",
    "from ipfml.processing import transform\n",
    "from ipfml import utils\n",
    "from PIL import Image\n",
    "from scipy import signal\n",
    "from skimage import color\n",
    "import cv2\n",
    "import scipy.stats as stats\n",
    "\n",
    "# display imports\n",
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "from mpl_toolkits.mplot3d import Axes3D\n",
    "\n",
    "# main imports\n",
    "import numpy as np\n",
    "import os"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_folder = \"../dataset\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Plane filter analysis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Utils functions definition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_images_path(dict_data):\n",
    "    \n",
    "    scene = dict_data['name']\n",
    "    prefix = dict_data['prefix']\n",
    "    indices = dict_data['indices']\n",
    "    \n",
    "    images_path = []\n",
    "    for index in indices:\n",
    "        path = os.path.join(data_folder, os.path.join(scene, prefix + index + \".png\"))\n",
    "        print(path)\n",
    "        images_path.append(path)\n",
    "    return images_path"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_images_zones(dict_data, images_path):\n",
    "    \n",
    "    id_zone = dict_data['zone']\n",
    "    \n",
    "    zones_img = []\n",
    "    for path in images_path:\n",
    "        img = Image.open(path)\n",
    "        zones = divide_in_blocks(img, (200, 200))\n",
    "        zones_img.append(zones[id_zone])\n",
    "        \n",
    "    return zones_img"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Scenes information data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# start 00020 - ref 00900 - step 10\n",
    "dict_appart = {'name': 'Appart1opt02', \n",
    "               'prefix': 'appartAopt_', \n",
    "               'indices': [\"00050\", \"00100\", \"00200\", \"00300\", \"00400\", \"00600\", \"00700\", \"00800\", \"00900\"],\n",
    "               'zone': 9}\n",
    "\n",
    "# start 00050 - ref 01200 - step 10\n",
    "dict_cuisine = {'name': 'Cuisine01', \n",
    "               'prefix': 'cuisine01_', \n",
    "               'indices': [\"00050\", \"00100\", \"00200\", \"00300\", \"00400\", \"00600\", \"00700\", \"00800\", \"00900\", \"01000\", \"01100\", \"01200\"],\n",
    "               'zone': 6}\n",
    "\n",
    "# start 00020 - ref 00950 - step 10\n",
    "dict_sdb_c = {'name': 'SdbCentre', \n",
    "               'prefix': 'SdB2_', \n",
    "               'indices': [\"00020\", \"00400\", \"00950\"],\n",
    "               'zone': 3}\n",
    "\n",
    "# start 00020 - ref 00950 - step 10\n",
    "dict_sdb_d = {'name': 'SdbDroite', \n",
    "               'prefix': 'SdB2_D_', \n",
    "               'indices': [\"00020\", \"00400\", \"00950\"],\n",
    "               'zone': 3}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Definition of parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "../dataset/Appart1opt02/appartAopt_00050.png\n",
      "../dataset/Appart1opt02/appartAopt_00100.png\n",
      "../dataset/Appart1opt02/appartAopt_00200.png\n",
      "../dataset/Appart1opt02/appartAopt_00300.png\n",
      "../dataset/Appart1opt02/appartAopt_00400.png\n",
      "../dataset/Appart1opt02/appartAopt_00600.png\n",
      "../dataset/Appart1opt02/appartAopt_00700.png\n",
      "../dataset/Appart1opt02/appartAopt_00800.png\n",
      "../dataset/Appart1opt02/appartAopt_00900.png\n",
      "../dataset/Cuisine01/cuisine01_00050.png\n",
      "../dataset/Cuisine01/cuisine01_00100.png\n",
      "../dataset/Cuisine01/cuisine01_00200.png\n",
      "../dataset/Cuisine01/cuisine01_00300.png\n",
      "../dataset/Cuisine01/cuisine01_00400.png\n",
      "../dataset/Cuisine01/cuisine01_00600.png\n",
      "../dataset/Cuisine01/cuisine01_00700.png\n",
      "../dataset/Cuisine01/cuisine01_00800.png\n",
      "../dataset/Cuisine01/cuisine01_00900.png\n",
      "../dataset/Cuisine01/cuisine01_01000.png\n",
      "../dataset/Cuisine01/cuisine01_01100.png\n",
      "../dataset/Cuisine01/cuisine01_01200.png\n"
     ]
    }
   ],
   "source": [
    "images_path_appart = compute_images_path(dict_appart)\n",
    "images_path_cuisine = compute_images_path(dict_cuisine)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "zones_appart = get_images_zones(dict_appart, images_path_appart)\n",
    "zones_cuisine = get_images_zones(dict_cuisine, images_path_cuisine)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# return residual information\n",
    "def plane_kernel_filter(window):\n",
    "    \n",
    "    width, height = window.shape\n",
    "\n",
    "    # prepare data\n",
    "    nb_elem = width * height\n",
    "    xs = [int(i/height) for i in range(nb_elem)]\n",
    "    ys = [i%height for i in range(nb_elem)]\n",
    "    zs = np.array(window).flatten().tolist()\n",
    "\n",
    "    # get residual (error) from mean plane computed\n",
    "    tmp_A = []\n",
    "    tmp_b = []\n",
    "\n",
    "    for i in range(len(xs)):\n",
    "        tmp_A.append([xs[i], ys[i], 1])\n",
    "        tmp_b.append(zs[i])\n",
    "\n",
    "    b = np.matrix(tmp_b).T\n",
    "    A = np.matrix(tmp_A)\n",
    "\n",
    "    fit = (A.T * A).I * A.T * b\n",
    "\n",
    "    errors = b - A * fit\n",
    "    residual = np.linalg.norm(errors)\n",
    "\n",
    "    return residual\n",
    "\n",
    "\n",
    "# return difference between min and max errors\n",
    "def plane_kernel_filter_max_error(window):\n",
    "    \n",
    "    width, height = window.shape\n",
    "\n",
    "    # prepare data\n",
    "    nb_elem = width * height\n",
    "    xs = [int(i/height) for i in range(nb_elem)]\n",
    "    ys = [i%height for i in range(nb_elem)]\n",
    "    zs = np.array(window).flatten().tolist()\n",
    "\n",
    "    # get residual (error) from mean plane computed\n",
    "    tmp_A = []\n",
    "    tmp_b = []\n",
    "\n",
    "    for i in range(len(xs)):\n",
    "        tmp_A.append([xs[i], ys[i], 1])\n",
    "        tmp_b.append(zs[i])\n",
    "\n",
    "    b = np.matrix(tmp_b).T\n",
    "    A = np.matrix(tmp_A)\n",
    "\n",
    "    fit = (A.T * A).I * A.T * b\n",
    "\n",
    "    errors = b - A * fit\n",
    "    residual = np.linalg.norm(errors)\n",
    "    \n",
    "    errors = abs(np.array(errors))\n",
    "\n",
    "    return (errors.max() - errors.min())\n",
    "\n",
    "\n",
    "def plane_custom_filter(img, kernel=(5, 5)):\n",
    "    \n",
    "    img = np.array(img)\n",
    "    \n",
    "    width, height = img.shape\n",
    "    \n",
    "    kernel_width, kernel_height = kernel\n",
    "    \n",
    "    if kernel_width % 2 == 0 or kernel_height % 2 == 0:\n",
    "        raise ValueError(\"Invalid kernel size, need to be of odd size\")\n",
    "        \n",
    "    padding_height = (kernel_width - 1) / 2\n",
    "    padding_width = (kernel_width - 1) / 2\n",
    "    \n",
    "    img_plane_error = []\n",
    "    for i in range(width):\n",
    "        \n",
    "        if i >= padding_width and i < (width - padding_width):\n",
    "            \n",
    "            row_plane_error = []\n",
    "            \n",
    "            for j in range (height):\n",
    "                \n",
    "                if j >= padding_height and j < (height - padding_height):\n",
    "                    \n",
    "                    # pixel in the center of kernel window size, need to extract window from img\n",
    "                    window = img[int(i-padding_width):int(i+padding_width + 1), int(j-padding_height):int(j+padding_height + 1)]\n",
    "                    \n",
    "                    diff = plane_kernel_filter(window)\n",
    "                    row_plane_error.append(diff)\n",
    "                    \n",
    "            img_plane_error.append(row_plane_error)\n",
    "        \n",
    "    return np.array(img_plane_error)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "def apply_on_zone(zone):\n",
    "    \n",
    "    sub_zones = divide_in_blocks(zone, (20, 20))\n",
    "\n",
    "    error_list = []\n",
    "\n",
    "    for sub_zone in sub_zones:\n",
    "        l_img = transform.get_LAB_L(sub_zone)\n",
    "        plane_error = plane_custom_filter(utils.normalize_2D_arr(l_img), (5, 5))\n",
    "        global_diff = np.std(plane_error)\n",
    "        error_list.append(plane_error)\n",
    "\n",
    "    error_list = np.array(error_list)\n",
    "    score = np.std(error_list[0:int(len(sub_zones)/5)])\n",
    "    print(score)\n",
    "    \n",
    "def apply_on_zone_plane_normed(zone, kernel=(5, 5)):\n",
    "    \n",
    "    l_img = transform.get_LAB_L(zone)\n",
    "    \n",
    "    plane_error = plane_custom_filter(utils.normalize_2D_arr(l_img), kernel)\n",
    "    return np.mean(plane_error)\n",
    "    \n",
    "def apply_on_zone_plane(zone, kernel=(5, 5)):\n",
    "    \n",
    "    l_img = transform.get_LAB_L(zone)\n",
    "    \n",
    "    plane_error = plane_custom_filter(l_img, kernel)\n",
    "    return np.mean(plane_error)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7fc3b51bf320>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 128224 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.imshow(zones_appart[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7fc3b194eeb8>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 127892 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.imshow(zones_cuisine[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def display_computed_data(zones, dict_scene):\n",
    "    \n",
    "    errors = []\n",
    "    errors_normed = []\n",
    "    \n",
    "    print(\"---------------------------------------------------------------------------------------\")\n",
    "    print(\"Compute error on \" + dict_scene[\"name\"] + \" scene (zone \" + str(dict_scene[\"zone\"]) + \")\") \n",
    "    \n",
    "    for index, zone in enumerate(zones):\n",
    "        plane_mean_error = apply_on_zone_plane(zone)\n",
    "        plane_mean_error_normed = apply_on_zone_plane_normed(zone)\n",
    "    \n",
    "        errors.append(plane_mean_error)\n",
    "        errors_normed.append(plane_mean_error_normed)\n",
    "        \n",
    "        print(dict_scene[\"prefix\"] +dict_scene[\"indices\"][index], \"=> score\",\"{0:.8f}\".format(plane_mean_error),\"| normed :\",\"{0:.8f}\".format(plane_mean_error_normed))\n",
    "        \n",
    "    return errors, errors_normed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---------------------------------------------------------------------------------------\n",
      "Compute error on Appart1opt02 scene (zone 9)\n",
      "appartAopt_00050 => score 13.30254427 | normed : 0.15702255\n",
      "appartAopt_00100 => score 11.09155512 | normed : 0.13641112\n",
      "appartAopt_00200 => score 9.53841914 | normed : 0.11535671\n",
      "appartAopt_00300 => score 8.93721088 | normed : 0.10914040\n",
      "appartAopt_00400 => score 8.57067712 | normed : 0.10599665\n",
      "appartAopt_00600 => score 8.16948645 | normed : 0.10148266\n",
      "appartAopt_00700 => score 8.04385333 | normed : 0.09958658\n",
      "appartAopt_00800 => score 7.96053962 | normed : 0.09888709\n",
      "appartAopt_00900 => score 7.89593901 | normed : 0.09808461\n",
      "---------------------------------------------------------------------------------------\n",
      "Compute error on Cuisine01 scene (zone 6)\n",
      "cuisine01_00050 => score 14.26744032 | normed : 0.15361529\n",
      "cuisine01_00100 => score 11.91046744 | normed : 0.12842483\n",
      "cuisine01_00200 => score 10.20911653 | normed : 0.11054688\n",
      "cuisine01_00300 => score 9.45622725 | normed : 0.10291345\n",
      "cuisine01_00400 => score 9.01093786 | normed : 0.09767920\n",
      "cuisine01_00600 => score 8.50241410 | normed : 0.09253297\n",
      "cuisine01_00700 => score 8.33067727 | normed : 0.09066393\n",
      "cuisine01_00800 => score 8.21224043 | normed : 0.08937497\n",
      "cuisine01_00900 => score 8.10144871 | normed : 0.08782028\n",
      "cuisine01_01000 => score 8.02322747 | normed : 0.08731791\n",
      "cuisine01_01100 => score 7.94861294 | normed : 0.08650587\n",
      "cuisine01_01200 => score 7.89148254 | normed : 0.08592211\n"
     ]
    }
   ],
   "source": [
    "appart_errors = display_computed_data(zones_appart, dict_appart)\n",
    "cuisine_errors = display_computed_data(zones_cuisine, dict_cuisine)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Performance indication"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8.48 s ± 267 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "8.66 s ± 293 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "9.56 s ± 679 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
     ]
    }
   ],
   "source": [
    "first_zone = zones_appart[0]\n",
    "%timeit plane_error_img = apply_on_zone_plane(first_zone, kernel=(3, 3))\n",
    "%timeit plane_error_img = apply_on_zone_plane(first_zone, kernel=(5, 5))\n",
    "%timeit plane_error_img = apply_on_zone_plane(first_zone, kernel=(7, 7))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "thesis-venv",
   "language": "python",
   "name": "thesis-venv"
  },
  "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.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
