Error_to_Image.ipynb 46.3 KB
Newer Older
Nathaniel Callens's avatar
Nathaniel Callens committed
1 2 3 4
{
 "cells": [
  {
   "cell_type": "code",
Kelly Chang's avatar
Kelly Chang committed
5
   "execution_count": 19,
Nathaniel Callens's avatar
Nathaniel Callens committed
6 7 8 9
   "id": "dbef8759",
   "metadata": {
    "id": "dbef8759"
   },
Kelly Chang's avatar
Kelly Chang committed
10 11 12 13 14 15 16 17 18 19 20 21 22
   "outputs": [
    {
     "ename": "ModuleNotFoundError",
     "evalue": "No module named 'prediction_MSE_kelly'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
      "\u001b[0;32m/var/folders/z2/plvrsqjs023g1cmx7k19mhzr0000gn/T/ipykernel_21377/700581042.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mnumpy\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mprediction_MSE_kelly\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mfile_extractor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mimage_extractor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mim_distribution\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mmatplotlib\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpyplot\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mitertools\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mproduct\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'prediction_MSE_kelly'"
     ]
    }
   ],
Nathaniel Callens's avatar
Nathaniel Callens committed
23 24 25 26 27 28 29 30 31 32 33 34 35
   "source": [
    "import numpy as np\n",
    "from prediction_MSE_Scout import file_extractor, image_extractor, im_distribution\n",
    "from matplotlib import pyplot as plt\n",
    "from itertools import product\n",
    "import os\n",
    "import sys\n",
    "from PIL import Image\n",
    "from scipy.optimize import minimize\n",
    "from time import time\n",
    "from numpy import linalg as la\n",
    "from scipy.stats import gaussian_kde\n",
    "import seaborn as sns\n",
Kelly Chang's avatar
Kelly Chang committed
36
    "#import pywt\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
37
    "from collections import Counter"
Nathaniel Callens's avatar
Nathaniel Callens committed
38 39 40 41
   ]
  },
  {
   "cell_type": "code",
Kelly Chang's avatar
Kelly Chang committed
42
<<<<<<< HEAD
Kelly Chang's avatar
Kelly Chang committed
43
   "execution_count": null,
Kelly Chang's avatar
Kelly Chang committed
44
=======
Nathaniel Callens's avatar
Nathaniel Callens committed
45
   "execution_count": 96,
Kelly Chang's avatar
Kelly Chang committed
46
>>>>>>> e119fdf48d4db1da99e5c8467b5c3ab9ac070ef5
Nathaniel Callens's avatar
Nathaniel Callens committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
   "id": "9ed20f84",
   "metadata": {
    "id": "9ed20f84"
   },
   "outputs": [],
   "source": [
    "def plot_hist(tiff_list, i=0):\n",
    "    \"\"\"\n",
    "    This function is the leftovers from the first attempt to plot histograms.\n",
    "    As it stands it needs some work in order to function again. We will\n",
    "    fix this later. 1/25/22\n",
    "    \"\"\"\n",
    "    \n",
    "    image = tiff_list[i]\n",
    "    image = Image.open(image)    #Open the image and read it as an Image object\n",
    "    image = np.array(image)[1:,:]    #Convert to an array, leaving out the first row because the first row is just housekeeping data\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
63
    "    image_int = image.astype(int)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
64 65 66
    "    \n",
    "    A = np.array([[3,0,-1],[0,3,3],[1,-3,-4]]) # the matrix for system of equation\n",
    "    \n",
Nathaniel Callens's avatar
Nathaniel Callens committed
67 68 69 70
    "    z0 = image_int[0:-2,0:-2]   # get all the first pixel for the entire image\n",
    "    z1 = image_int[0:-2,1:-1]   # get all the second pixel for the entire image\n",
    "    z2 = image_int[0:-2,2::]    # get all the third pixel for the entire image\n",
    "    z3 = image_int[1:-1,0:-2]   # get all the fourth pixel for the entire image\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
71 72 73 74 75 76 77 78
    "    \n",
    "    # calculate the out put of the system of equation\n",
    "    y0 = np.ravel(-z0+z2-z3)\n",
    "    y1 = np.ravel(z0+z1+z2)\n",
    "    y2 = np.ravel(-z0-z1-z2-z3)\n",
    "    y = np.vstack((y0,y1,y2))\n",
    "    \n",
    "    # use numpy solver to solve the system of equations all at once\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
79 80
    "    #predict = np.linalg.solve(A,y)[-1]\n",
    "    predict = np.floor(np.linalg.solve(A,y)[-1]).astype(int)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
81 82
    "    #predict = []\n",
    "    \n",
Nathaniel Callens's avatar
Nathaniel Callens committed
83
    "    # flatten the neighbor pixels and stack them together\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
84 85 86 87 88 89 90 91
    "    z0 = np.ravel(z0)\n",
    "    z1 = np.ravel(z1)\n",
    "    z2 = np.ravel(z2)\n",
    "    z3 = np.ravel(z3)\n",
    "    neighbor = np.vstack((z0,z1,z2,z3)).T\n",
    "    \n",
    "    # calculate the difference\n",
    "    diff = np.max(neighbor,axis = 1) - np.min(neighbor, axis=1)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
92
    "    diff = np.pad(diff.reshape(510,638), pad_width=1)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
93 94
    "            \n",
    "    # flatten the image to a vector\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
95
    "    small_image = image_int[1:-1,1:-1]\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
96 97 98
    "    \n",
    "    #Reshape the predictions to be a 2D array\n",
    "    predict = np.pad(predict.reshape(510,638), pad_width=1)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
99
    "    \"\"\"predict[0,:] = image[0,:]\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
100 101
    "    predict[:,0] = image[:,0]\n",
    "    predict[:,-1] = image[:,-1]\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
102
    "    predict[-1,:] = image[-1,:]\"\"\"\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
103 104 105 106 107
    "    \n",
    "    \n",
    "    #Calculate the error between the original image and our predictions\n",
    "    #Note that we only predicted on the inside square of the original image, excluding\n",
    "    #The first row, column and last row, column\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
108 109 110 111
    "    #error = (image_int - predict).astype(int) #Experiment\n",
    "    \n",
    "    #this one works\n",
    "    error = image_int - predict\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
112 113
    "\n",
    "    \n",
Nathaniel Callens's avatar
Nathaniel Callens committed
114
    "    return predict, diff, image_int, error, A"
Nathaniel Callens's avatar
Nathaniel Callens committed
115 116 117 118
   ]
  },
  {
   "cell_type": "code",
Kelly Chang's avatar
Kelly Chang committed
119
<<<<<<< HEAD
Kelly Chang's avatar
Kelly Chang committed
120
   "execution_count": null,
Kelly Chang's avatar
Kelly Chang committed
121
=======
Nathaniel Callens's avatar
Nathaniel Callens committed
122
   "execution_count": 202,
Kelly Chang's avatar
Kelly Chang committed
123
>>>>>>> e119fdf48d4db1da99e5c8467b5c3ab9ac070ef5
Nathaniel Callens's avatar
Nathaniel Callens committed
124
   "id": "ba2881d9",
Nathaniel Callens's avatar
Nathaniel Callens committed
125 126 127 128 129
   "metadata": {},
   "outputs": [],
   "source": [
    "scenes = file_extractor()\n",
    "images = image_extractor(scenes)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
130
    "num_images = im_distribution(images, \"11\")"
Nathaniel Callens's avatar
Nathaniel Callens committed
131 132 133 134
   ]
  },
  {
   "cell_type": "code",
Kelly Chang's avatar
Kelly Chang committed
135
<<<<<<< HEAD
Kelly Chang's avatar
Kelly Chang committed
136
   "execution_count": 2,
Kelly Chang's avatar
Kelly Chang committed
137
=======
Nathaniel Callens's avatar
Nathaniel Callens committed
138
   "execution_count": 203,
Kelly Chang's avatar
Kelly Chang committed
139
>>>>>>> e119fdf48d4db1da99e5c8467b5c3ab9ac070ef5
Nathaniel Callens's avatar
Nathaniel Callens committed
140
   "id": "11e95c34",
Nathaniel Callens's avatar
Nathaniel Callens committed
141
   "metadata": {},
Kelly Chang's avatar
Kelly Chang committed
142 143 144 145 146 147 148 149 150 151 152 153 154
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'plot_hist' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m/var/folders/z2/plvrsqjs023g1cmx7k19mhzr0000gn/T/ipykernel_21377/1723356297.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mpredict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdiff\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mim\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mA\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplot_hist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimages\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m: name 'plot_hist' is not defined"
     ]
    }
   ],
Nathaniel Callens's avatar
Nathaniel Callens committed
155
   "source": [
Nathaniel Callens's avatar
Nathaniel Callens committed
156
    "predict, diff, im, err, A = plot_hist(images, 2)"
Nathaniel Callens's avatar
Nathaniel Callens committed
157 158 159 160
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
161
   "execution_count": 228,
Nathaniel Callens's avatar
Nathaniel Callens committed
162
   "id": "434e4d2f",
Nathaniel Callens's avatar
Nathaniel Callens committed
163 164 165 166 167 168 169 170 171 172 173 174 175
   "metadata": {},
   "outputs": [],
   "source": [
    "def reconstruct(error, A):\n",
    "    \"\"\"\n",
    "    Function that reconstructs the original image\n",
    "    from the error matrix and using the predictive\n",
    "    algorithm developed in the encoding.\n",
    "    \n",
    "    Parameters:\n",
    "        error (array): matrix of errors computed in encoding. Same \n",
    "                       shape as the original image (512, 640) in this case\n",
    "        A (array): Matrix used for the system of equations to create predictions\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
176
    "    Returns:\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
177 178
    "        image (array): The reconstructed image\n",
    "    \"\"\"\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
179 180 181 182 183 184 185
    "    new_e = error.copy()\n",
    "    rows, columns = new_e.shape\n",
    "\n",
    "    for r in range(1, rows-1):\n",
    "        for c in range(1, columns-1):\n",
    "            z0, z1, z2, z3 = new_e[r-1][c-1], new_e[r-1][c], new_e[r-1][c+1], new_e[r][c-1]\n",
    "            y = np.vstack((-z0+z2-z3, z0+z1+z2, -z0-z1-z2-z3))\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
186
    "            \n",
Nathaniel Callens's avatar
Nathaniel Callens committed
187
    "            new_e[r][c] = np.round(new_e[r][c] + np.linalg.solve(A,y)[-1], 1)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
188 189
    "            \n",
    "    return new_e.astype(int)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
190
    "    "
Nathaniel Callens's avatar
Nathaniel Callens committed
191 192 193 194
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
195
   "execution_count": 210,
Nathaniel Callens's avatar
Nathaniel Callens committed
196
   "id": "3cc609dc",
Nathaniel Callens's avatar
Nathaniel Callens committed
197
   "metadata": {},
Nathaniel Callens's avatar
Nathaniel Callens committed
198 199 200 201 202
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
Nathaniel Callens's avatar
Nathaniel Callens committed
203 204 205 206
      "22483 22521 22503 22481\n",
      "[22491.]\n",
      "-9\n",
      "[22482.]\n"
Nathaniel Callens's avatar
Nathaniel Callens committed
207 208 209
     ]
    }
   ],
Nathaniel Callens's avatar
Nathaniel Callens committed
210 211 212 213 214 215
   "source": [
    "new_error = reconstruct(err, A)"
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
216
   "execution_count": 211,
Nathaniel Callens's avatar
Nathaniel Callens committed
217 218 219 220 221 222
   "id": "5d290a0c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
Nathaniel Callens's avatar
Nathaniel Callens committed
223 224 225
       "array([[ True,  True,  True, ...,  True,  True,  True],\n",
       "       [ True,  True,  True, ...,  True,  True,  True],\n",
       "       [ True,  True,  True, ...,  True,  True,  True],\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
226
       "       ...,\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
227 228 229
       "       [ True,  True,  True, ...,  True,  True,  True],\n",
       "       [ True,  True,  True, ...,  True,  True,  True],\n",
       "       [ True,  True,  True, ...,  True,  True,  True]])"
Nathaniel Callens's avatar
Nathaniel Callens committed
230 231
      ]
     },
Nathaniel Callens's avatar
Nathaniel Callens committed
232
     "execution_count": 211,
Nathaniel Callens's avatar
Nathaniel Callens committed
233 234 235 236 237
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
Nathaniel Callens's avatar
Nathaniel Callens committed
238
    "im == new_error"
Nathaniel Callens's avatar
Nathaniel Callens committed
239 240 241 242
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
243
   "execution_count": 9,
Nathaniel Callens's avatar
Nathaniel Callens committed
244
   "id": "706f2816",
Nathaniel Callens's avatar
Nathaniel Callens committed
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
   "metadata": {},
   "outputs": [],
   "source": [
    "first = []\n",
    "second = []\n",
    "third = []\n",
    "fourth = []\n",
    "\n",
    "for i in range(1,diff.shape[0]-1):\n",
    "    for j in range(1,diff.shape[1]-1):\n",
    "        if diff[i][j] <= 50:\n",
    "            first.append(np.abs(err[i][j]))\n",
    "        elif diff[i][j] > 50 and diff[i][j] <= 100:\n",
    "            second.append(np.abs(err[i][j]))\n",
    "        elif diff[i][j] > 100 and diff[i][j] <= 200:\n",
    "            third.append(np.abs(err[i][j]))\n",
    "        else:\n",
    "            fourth.append(np.abs(err[i][j]))\n"
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
267
   "execution_count": 10,
Nathaniel Callens's avatar
Nathaniel Callens committed
268
   "id": "530d2cab",
Nathaniel Callens's avatar
Nathaniel Callens committed
269 270 271 272
   "metadata": {},
   "outputs": [
    {
     "data": {
Nathaniel Callens's avatar
Nathaniel Callens committed
273
      "image/png": "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\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
274
      "text/plain": [
Nathaniel Callens's avatar
Nathaniel Callens committed
275
       "<Figure size 432x288 with 1 Axes>"
Nathaniel Callens's avatar
Nathaniel Callens committed
276 277
      ]
     },
Nathaniel Callens's avatar
Nathaniel Callens committed
278 279 280 281 282 283 284 285 286
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
Nathaniel Callens's avatar
Nathaniel Callens committed
287
      "142\n"
Nathaniel Callens's avatar
Nathaniel Callens committed
288 289 290 291
     ]
    },
    {
     "data": {
Nathaniel Callens's avatar
Nathaniel Callens committed
292
      "image/png": "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\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
293 294 295 296 297 298 299 300 301 302 303 304 305
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
Nathaniel Callens's avatar
Nathaniel Callens committed
306
      "154\n"
Nathaniel Callens's avatar
Nathaniel Callens committed
307 308 309 310
     ]
    },
    {
     "data": {
Nathaniel Callens's avatar
Nathaniel Callens committed
311
      "image/png": "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\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
312 313 314 315 316 317 318 319 320 321 322 323 324
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
Nathaniel Callens's avatar
Nathaniel Callens committed
325
      "217\n"
Nathaniel Callens's avatar
Nathaniel Callens committed
326 327 328 329
     ]
    },
    {
     "data": {
Nathaniel Callens's avatar
Nathaniel Callens committed
330
      "image/png": "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\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
331 332 333 334 335 336 337 338 339 340 341 342 343
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
Nathaniel Callens's avatar
Nathaniel Callens committed
344
      "176\n"
Nathaniel Callens's avatar
Nathaniel Callens committed
345
     ]
Nathaniel Callens's avatar
Nathaniel Callens committed
346 347 348
    }
   ],
   "source": [
Nathaniel Callens's avatar
Nathaniel Callens committed
349 350 351 352 353 354 355 356 357 358 359 360 361
    "\n",
    "plt.hist(first)\n",
    "plt.show()\n",
    "print(np.max(first))\n",
    "plt.hist(second)\n",
    "plt.show()\n",
    "print(np.max(second))\n",
    "plt.hist(third)\n",
    "plt.show()\n",
    "print(np.max(third))\n",
    "plt.hist(fourth)\n",
    "plt.show()\n",
    "print(np.max(fourth))"
Nathaniel Callens's avatar
Nathaniel Callens committed
362
   ]
Nathaniel Callens's avatar
Nathaniel Callens committed
363 364 365
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
366
   "execution_count": 11,
Nathaniel Callens's avatar
Nathaniel Callens committed
367
   "id": "bb11dcd0",
Nathaniel Callens's avatar
Nathaniel Callens committed
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
   "metadata": {},
   "outputs": [],
   "source": [
    "class NodeTree(object):\n",
    "    def __init__(self, left=None, right=None):\n",
    "        self.left = left\n",
    "        self.right = right\n",
    "\n",
    "    def children(self):\n",
    "        return self.left, self.right\n",
    "\n",
    "    def __str__(self):\n",
    "        return self.left, self.right\n",
    "\n",
    "\n",
    "def huffman_code_tree(node, binString=''):\n",
    "    '''\n",
    "    Function to find Huffman Code\n",
    "    '''\n",
    "    if type(node) is str:\n",
    "        return {node: binString}\n",
    "    (l, r) = node.children()\n",
    "    d = dict()\n",
    "    d.update(huffman_code_tree(l, binString + '0'))\n",
    "    d.update(huffman_code_tree(r, binString + '1'))\n",
    "    return d\n",
    "\n",
    "\n",
    "def make_tree(nodes):\n",
    "    '''\n",
    "    Function to make tree\n",
    "    :param nodes: Nodes\n",
    "    :return: Root of the tree\n",
    "    '''\n",
    "    while len(nodes) > 1:\n",
    "        (key1, c1) = nodes[-1]\n",
    "        (key2, c2) = nodes[-2]\n",
    "        nodes = nodes[:-2]\n",
    "        node = NodeTree(key1, key2)\n",
    "        nodes.append((node, c1 + c2))\n",
    "        nodes = sorted(nodes, key=lambda x: x[1], reverse=True)\n",
    "    return nodes[0][0]"
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
414
   "execution_count": 102,
Nathaniel Callens's avatar
Nathaniel Callens committed
415
   "id": "c01fda28",
Nathaniel Callens's avatar
Nathaniel Callens committed
416 417 418 419 420
   "metadata": {},
   "outputs": [],
   "source": [
    "def enc_experiment(images, plot=True):\n",
    "    origin, predict, diff, error, A = plot_hist(images, 2)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
421
    "    image = Image.open(images[2])    #Open the image and read it as an Image object\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
    "    image = np.array(image)[1:,:]    #Convert to an array, leaving out the first row because the first row is just housekeeping data\n",
    "    image = image.astype(int)\n",
    "    new_error = np.copy(image)\n",
    "    #new_error[1:-1,1:-1] = np.reshape(error[1:-1,1:-1],(510, 638))\n",
    "    new_error[1:-1, 1:-1] = error[1:-1, 1:-1]\n",
    "    keep = new_error[0,0]\n",
    "    new_error[0,:] = new_error[0,:] - keep\n",
    "    new_error[-1,:] = new_error[-1,:] - keep\n",
    "    new_error[1:-1,0] = new_error[1:-1,0] - keep\n",
    "    new_error[1:-1,-1] = new_error[1:-1,-1] - keep\n",
    "    new_error[0,0] = keep\n",
    "    new_error = np.ravel(new_error)\n",
    "    if plot:\n",
    "        plt.hist(new_error[1:],bins=100)\n",
    "        plt.show()\n",
    "    \n",
    "    #ab_error = np.abs(new_error)\n",
    "    #string = [str(i) for i in ab_error]\n",
    "    string = [str(i) for i in new_error]\n",
    "    #string = [str(i) for i in np.arange(0,5)] + [str(i) for i in np.arange(0,5)] + [str(i) for i in np.arange(0,2)]*2\n",
    "    freq = dict(Counter(string))\n",
    "    freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)\n",
    "    \n",
    "    node = make_tree(freq)\n",
    "    encoding_dict = huffman_code_tree(node)\n",
    "    #encoded = [\"1\"+encoding[str(-i)] if i < 0 else \"0\"+encoding[str(i)] for i in error]\n",
    "    #print(time.time()-start)\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
449 450
    "    encoded = new_error.reshape((512,640)).copy().astype(str).astype(object)\n",
    "\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
451 452 453 454 455 456 457 458
    "    for i in range(encoded.shape[0]):\n",
    "        for j in range(encoded.shape[1]):\n",
    "            if i == 0 and j == 0:\n",
    "                encoded[i][j] = encoded[i][j]\n",
    "            else:\n",
    "                #print(encoding_dict[encoded[i][j]])\n",
    "                encoded[i][j] = encoding_dict[encoded[i][j]]\n",
    "                #print(encoded[i][j])\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
459 460
    "                \n",
    "    return encoding_dict, encoded, new_error.reshape((512,640)), image\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
461 462 463 464 465
    "    #print(encoding)"
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
466
   "execution_count": 103,
Nathaniel Callens's avatar
Nathaniel Callens committed
467
   "id": "ffa858e8",
Nathaniel Callens's avatar
Nathaniel Callens committed
468
   "metadata": {},
Nathaniel Callens's avatar
Nathaniel Callens committed
469
   "outputs": [],
Nathaniel Callens's avatar
Nathaniel Callens committed
470
   "source": [
Nathaniel Callens's avatar
Nathaniel Callens committed
471
    "encode_dict, encoding, error, orig_image = enc_experiment(images, plot=False)"
Nathaniel Callens's avatar
Nathaniel Callens committed
472 473 474 475
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
476
   "execution_count": 104,
Nathaniel Callens's avatar
Nathaniel Callens committed
477
   "id": "8dfdedc6",
Nathaniel Callens's avatar
Nathaniel Callens committed
478 479 480
   "metadata": {},
   "outputs": [
    {
Nathaniel Callens's avatar
Nathaniel Callens committed
481 482 483 484 485 486 487 488
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
Nathaniel Callens's avatar
Nathaniel Callens committed
489 490 491
    }
   ],
   "source": [
Nathaniel Callens's avatar
Nathaniel Callens committed
492
    "error[1,6]"
Nathaniel Callens's avatar
Nathaniel Callens committed
493 494 495 496
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
497
   "execution_count": 218,
Nathaniel Callens's avatar
Nathaniel Callens committed
498
   "id": "825cc48c",
Nathaniel Callens's avatar
Nathaniel Callens committed
499 500 501 502 503 504 505 506
   "metadata": {},
   "outputs": [],
   "source": [
    "def decoder(A, encoded_matrix, encoding_dict):\n",
    "    \"\"\"\n",
    "    Function that accecpts the prediction matrix A for the linear system,\n",
    "    the encoded matrix of error values, and the encoding dicitonary.\n",
    "    \"\"\"\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
507 508
    "    the_keys = list(encode_dict.keys())\n",
    "    the_values = list(encode_dict.values())\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
509
    "    error_matrix = encoded_matrix.copy()\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
510
    "    \n",
Nathaniel Callens's avatar
Nathaniel Callens committed
511 512 513
    "    for i in range(error_matrix.shape[0]):\n",
    "        for j in range(error_matrix.shape[1]):\n",
    "            if i == 0 and j == 0:\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
514
    "                error_matrix[i][j] = int(encoded_matrix[i][j])\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
515
    "                \n",
Nathaniel Callens's avatar
Nathaniel Callens committed
516 517
    "            elif i == 0 or i == error_matrix.shape[0]-1 or j == 0 or j == error_matrix.shape[1]-1:\n",
    "                error_matrix[i][j] = int(the_keys[the_values.index(error_matrix[i,j])]) + error_matrix[0][0]\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
518
    "            else:\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
519 520 521 522 523
    "                \"\"\"z0, z1, z2, z3 = error_matrix[i-1][j-1], error_matrix[i-1][j], \\\n",
    "                error_matrix[i-1][j+1], error_matrix[i][j-1]\n",
    "                y = np.vstack((-z0+z2-z3, z0+z1+z2, -z0-z1-z2-z3))\"\"\"\n",
    "                \n",
    "                error_matrix[i][j] = int(the_keys[the_values.index(error_matrix[i,j])])\n",
Nathaniel Callens's avatar
Nathaniel Callens committed
524
    "                \n",
Nathaniel Callens's avatar
Nathaniel Callens committed
525
    "    return error_matrix.astype(int)"
Nathaniel Callens's avatar
Nathaniel Callens committed
526
   ]
Nathaniel Callens's avatar
Nathaniel Callens committed
527 528 529
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
530
   "execution_count": 219,
Nathaniel Callens's avatar
Nathaniel Callens committed
531 532
   "id": "ba1d2c2c",
   "metadata": {},
Nathaniel Callens's avatar
Nathaniel Callens committed
533 534 535 536 537 538 539 540 541 542
   "outputs": [],
   "source": [
    "em = decoder(A, encoding, encode_dict)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 220,
   "id": "b2cdce6d",
   "metadata": {},
Nathaniel Callens's avatar
Nathaniel Callens committed
543 544 545 546 547
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
Nathaniel Callens's avatar
Nathaniel Callens committed
548 549 550 551
      "22483 22521 22503 22481\n",
      "[22491.]\n",
      "-9\n",
      "[22482.]\n"
Nathaniel Callens's avatar
Nathaniel Callens committed
552 553 554 555
     ]
    }
   ],
   "source": [
Nathaniel Callens's avatar
Nathaniel Callens committed
556 557
    "hopefully = reconstruct(em, A)\n",
    "#22487 22483 22521 22464"
Nathaniel Callens's avatar
Nathaniel Callens committed
558 559 560 561
   ]
  },
  {
   "cell_type": "code",
Nathaniel Callens's avatar
Nathaniel Callens committed
562 563
   "execution_count": 227,
   "id": "2d2506c9",
Nathaniel Callens's avatar
Nathaniel Callens committed
564
   "metadata": {},
Nathaniel Callens's avatar
Nathaniel Callens committed
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ True,  True,  True, ...,  True,  True,  True],\n",
       "       [ True,  True,  True, ...,  True,  True,  True],\n",
       "       [ True,  True,  True, ...,  True,  True,  True],\n",
       "       ...,\n",
       "       [ True,  True,  True, ...,  True,  True,  True],\n",
       "       [ True,  True,  True, ...,  True,  True,  True],\n",
       "       [ True,  True,  True, ...,  True,  True,  True]])"
      ]
     },
     "execution_count": 227,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hopefully == im"
   ]
Nathaniel Callens's avatar
Nathaniel Callens committed
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "Wavelet_Huffman.ipynb",
   "provenance": []
  },
  "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",
Kelly Chang's avatar
Kelly Chang committed
609
   "version": "3.9.1"
Nathaniel Callens's avatar
Nathaniel Callens committed
610 611 612 613 614
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}