diff --git a/jupyter.ipynb b/jupyter.ipynb index 0b3c7e9..9cd5f4b 100644 --- a/jupyter.ipynb +++ b/jupyter.ipynb @@ -2,8 +2,34 @@ "cells": [ { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 18, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/tq/jq5nwbnj7v10tls99x99qbh40000gn/T/ipykernel_66956/17056051.py:11: DeprecationWarning: Importing display from IPython.core.display is deprecated since IPython 7.14, please import from IPython display\n", + " from IPython.core.display import display, HTML\n" + ] + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import IPython\n", "import numpy as np\n", @@ -13,86 +39,82 @@ "from skimage import img_as_ubyte,img_as_float\n", "from numpy.linalg import svd\n", "from PIL import Image\n", + "from ipywidgets import interact\n", "# change the cell width\n", "from IPython.core.display import display, HTML\n", "display(HTML(\"\"))\n" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "#This line is required to display visualizations in the browser\n", "%matplotlib inline" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "### SVD and Image compression" - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "# Kompresja obrazów z wykorzystaniem rozkładu SVD" + ] }, { "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, "source": [ - "Now we will explore how to apply Singular Value Decomposition of a matrix to the problem of image compression. SVD decomposes a rectangular matrix $M$ to a three parts.\n", + "Metodę SVD zastosujemy do kompresji obrazów. SVD dokonuje dekompozycji macierzy prostokątnej $M$ na trzy części.\n", "$M=U\\Sigma V^T$ -\n", - "- $U$ - matrix of left singular vectors in the columns\n", - "- $\\Sigma$ - diagonal matrix with singular values\n", - "- $V$ - matrix of right singular vectors in the columns\n", + "- $U$ - macierz, w której kolumny są wektorami własnymi macierzy $MM^T$ (lewe wektory osobliwe - 'left singular vectors')\n", + "- $\\Sigma$ - macierz diagonalna, która na swojej diagonalii ma nieujemne wartości osobliwe (pierwiastki wartości własnych) macierzy $M^TM$ uporządkowane nierosnąco\n", + "- $V$ - macierz, w której kolumny są wektorami własnymi macierzy $M^TM$ (prawe wektory osobliwe - 'right singular vectors')\n", "\n", - "SVD in effect involves reconstructing the original matrix as a linear combination of several rank one matrices. A rank one matrix can be expressed as a outer product of two column vectors. \n", + "SVD polega na rekonstrukcji oryginalnej macierzy jako kombinacji liniowej kilku macierzy rangi jeden. Macierz rangi jeden można wyrazić jako iloczyn zewnętrzny dwóch wektorów kolumnowych. \n", "\n", "$M=\\sigma_1u_1v_1^T+\\sigma_2u_2v_2^T+\\sigma_3u_3v_3^T+\\sigma_3u_3v_3^T+....$ . \n", - "A matrix of rank r will have r terms of these.\n", + "Macierz o randze r będzie miała r takich wyrazów.\n", "\n", - "Here $\\sigma_1,\\sigma_2,\\sigma_3 ...$ are singular values. $u_1,u_2,u_3 ...$ and $v_1,v_2,v_3 ...$ are left and right singular vectors respectively.\n", + "Tutaj $\\sigma_1,\\sigma_2,\\sigma_3 ...$ są wartościami osobliwymi. $u_1,u_2,u_3 ...$ i $v_1,v_2,v_3 ...$ są kolejnymi kolumnami (wektorami własnymi) z macierzy U i V.\n", "\n", - "Image compression using SVD involves taking advantage of the fact that very few of the singular values are large. Although images from the real world are of full rank, they have low effective rank which means that only few of the singular values of the SVD of images will be large." - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } + "Kompresja obrazu przy użyciu SVD polega na wykorzystaniu faktu, że tylko nieliczne wartości osobliwe są duże. Chociaż obrazy ze świata rzeczywistego mają pełną rangę, to ich efektywna ranga jest niska, co oznacza, że tylko kilka wartości osobliwych rozkładu SVD obrazów będzie dużych." + ] }, { "cell_type": "markdown", - "source": [ - "### skimage image processing library\n", - "\n", - "We will use skimage image processing library (from sci-kit family of packages) for working with images in python. skimage has a module called data which makes available a set of images for exploration. We will load some images and convert them into a gray scale format. These images are stored in a python dict object gray_images." - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "### skimage - biblioteka do przetwarzania obrazów\n", + "\n", + "Do pracy z obrazami w Pythonie używamy biblioteki do przetwarzania obrazów ```skimage``` (z rodziny pakietów ```sci-kit```). Ma ona moduł o nazwie data, który udostępnia zestaw obrazów. Wczytujemy kilka obrazów i konwertujemy je do formatu skali szarości. Obrazy te są przechowywane w słowniku ```gray_images```." + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "gray_images = {\n", @@ -104,38 +126,36 @@ " \"blobs\":data.binary_blobs(),\n", " \"coffee\":rgb2gray(img_as_float(data.coffee()))\n", "}" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "### svd in python\n", - "We will use ```numpy.linalg``` library's ```svd``` function to compute svd of a matrix in python. The svd function returns U,s,V .\n", - " - U has left singular vectors in the columns\n", - " - s is rank 1 numpy array with singular values\n", - " - V has right singular vectors in the rows -equivalent to $V^T$ in traditional linear algebra literature\n", - " \n", - "The reconstructed approximation of the original matrix is done using a subset of singular vectors as below in the ```compress_svd``` function . We use numpy array slicing to select k singular vectors and values. Instead of storing $m\\times n$ values for the original image, we can now store $k(m+n)+k$ values\n", - "\n", - " reconst_matrix = np.dot(U[:,:k],np.dot(np.diag(s[:k]),V[:k,:]))\n", - " " - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "### SVD w python'ie\n", + "Używamy funkcji ```svd``` z biblioteki ```numpy.linalg``` do obliczenia rozkładu SVD naszej macierzy. Funkcja svd zwraca U,s,V .\n", + " - U macierz, w której kolumny są wektorami własnymi macierzy $MM^T$\n", + " - s jest tablicą numpy rangi 1 z wartościami osobliwymi\n", + " - V macierz, w której wiersze są wektorami własnymi macierzy $M^TM$ - odpowiednik $V^T$ w tradycyjnej literaturze algebry liniowej\n", + " \n", + "Zrekonstruowana aproksymacja oryginalnej macierzy jest wykonywana przy użyciu podzbioru wektorów osobliwych, jak poniżej w funkcji ``compress_svd``. Używamy ```NumPy Array Slicing```, aby wybrać k wektorów osobliwych i wartości osobliwych. Zamiast przechowywać $m\\times n$ wartości dla oryginalnego obrazu, możemy teraz przechowywać $k(m+n)+k$ wartości\n", + "\n", + " reconst_matrix = np.dot(U[:,:k],np.dot(np.diag(s[:k]),V[:k,:]))\n", + " " + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "def compress_svd(image,k):\n", @@ -149,30 +169,28 @@ " reconst_matrix = np.dot(U[:,:k],np.dot(np.diag(s[:k]),V[:k,:]))\n", "\n", " return reconst_matrix,s" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "### Compress gray scale images\n", - "The function ```compress_show_gray_images``` below takes in the image name (img_name) and number of singular values/vectors(k) to be used in the compressed reconstruction. It also plots the singular values and the image." - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "### Kompresja obrazów w skali szarości\n", + "Poniższa funkcja ``compress_show_gray_images`` przyjmuje nazwę obrazu (img_name) i liczbę wartości/wektorów osobliwych (k), które mają być użyte w skompresowanej rekonstrukcji. Na wykresie są wartości osobliwe oraz obraz." + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "def compress_show_gray_images(img_name,k):\n", @@ -190,29 +208,27 @@ " axes[1].imshow(reconst_img,cmap='gray')\n", " axes[1].axis('off')\n", " fig.tight_layout()" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "Use the below interactive widget to explore how the quality of the reconstructed image varies with $k$" - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "W celu zbadania, jak jakość zrekonstruowanego obrazu zmienia się wraz z $k$ należy użyć poniższego interaktywnego widżetu." + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "def compute_k_max(img_name):\n", @@ -232,17 +248,16 @@ " img_name=list_widget.value\n", " int_slider_widget.max = compute_k_max(img_name)\n", "list_widget.observe(update_k_max,'value')\n" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "# Print matrices\n", @@ -260,57 +275,93 @@ " print('*' * 100)\n", " print(f\"Shape of V matrix: {V[:k,:].shape}\")\n", " print(f\"V MATRIX: {V[:k,:]}\")\n" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 25, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3909141856c04409b28f9d02306ef5a6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='img_name', options=('cat', 'astro', 'camera', 'coin', 'clock', 'bl…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "interact(print_matrices, img_name=list_widget, k=int_slider_widget)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "interact(compress_show_gray_images,img_name=list_widget,k=int_slider_widget);" - ], + "execution_count": 26, "metadata": { - "collapsed": false, "pycharm": { "name": "#%%\n" } - } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7ca89c134f914dc9bcd4372a352eae5a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='img_name', options=('cat', 'astro', 'camera', 'coin', 'clock', 'bl…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "interact(compress_show_gray_images,img_name=list_widget,k=int_slider_widget);" + ] }, { "cell_type": "markdown", - "source": [ - "### Load color images" - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "### Ładowanie kolorowych obrazów" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "color_images = {\n", @@ -320,53 +371,52 @@ " \"koala\": img_as_float(Image.open('koala.jpeg')),\n", " \"orange\": img_as_float(Image.open('orange.jpeg'))\n", "}" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "### Compress color images\n", - "\n", - "Color images are represented in python as 3 dimensional numpy arrays - the third dimension to represent the color values (red,green blue). However, svd method is applicable to two dimensional matrices. So we have to find a way to convert the 3 dimensional array to 2 dimensional arrays, apply svd and reconstruct it back as a 3 dimensional array . There are two ways to do it. We will show both these methods below .\n", - " - reshape method\n", - " - Layer method\n" - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "### Kompresja kolorowych obrazów\n", + "\n", + "Obrazy kolorowe są reprezentowane w Pythonie jako trójwymiarowe tablice numpy - trzeci wymiar reprezentuje wartości kolorów (czerwony, zielony, niebieski). Jednak metoda svd ma zastosowanie do macierzy dwuwymiarowych. W tym celu musimy przekonwertować tablicę trójwymiarową na tablicę dwuwymiarową, zastosować svd i zrekonstruować ją z powrotem jako tablicę trójwymiarową. Istnieją dwa sposoby, aby to zrobić: \n", + " - Metoda reshape\n", + " - Metoda layers (warstwowa)\n", + "\n", + "Obie te metody pokażemy poniżej.\n" + ] }, { "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, "source": [ - "#### Reshape method to compress a color image\n", - "This method involves flattening the third dimension of the image array into the second dimension using numpy's reshape method .\n", + "#### Metoda reshape do kompresji kolorowych obrazów\n", + " \n", + "Metoda ta polega na spłaszczeniu trzeciego wymiaru tablicy obrazów do drugiego wymiaru przy użyciu metody ```reshape``` z biblioteki ```numpy```.\n", " \n", " ``` image_reshaped = image.reshape((original_shape[0],original_shape[1]*3))```\n", "\n", - "The svd decomposition is applied on the resulting reshaped array and reconstructed with the desired number of singular values/vectors. The image array is reshaped back to the three dimensions by another call to reshape method.\n", + "Dekompozycja svd jest stosowana do wynikowej, przekształconej tablicy i rekonstruowana z żądaną liczbą wartości/wektorów osobliwych. Tablica obrazów jest przekształcana z powrotem do trzech wymiarów przez kolejne wywołanie metody reshape.\n", " \n", - " ```image_reconst = image_reconst.reshape(original_shape)```\n", - "\n" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } + " ```image_reconst = image_reconst.reshape(original_shape)```" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "def compress_show_color_images_reshape(img_name,k):\n", @@ -381,29 +431,27 @@ " compression_ratio =100.0* (k*(original_shape[0] + 3*original_shape[1])+k)/(original_shape[0]*original_shape[1]*original_shape[2])\n", " plt.title(\"compression ratio={:.2f}\".format(compression_ratio)+\"%\")\n", " plt.imshow(image_reconst)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "Here is the interactive widget to explore image compression of color images using the reshape method. By dragging the slider to vary $k$, observe how image quality varies. Also, we can explore different images by selecting through the drop down widget." - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "Oto interaktywny widżet do badania kompresji obrazów kolorowych metodą reshape. Przeciągając suwak w celu zmiany wartości $k$, można zaobserwować, jak zmienia się jakość obrazu. Można także badać różne obrazy, wybierając je za pomocą rozwijanego widżetu." + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "def compute_k_max_color_images(img_name):\n", @@ -418,71 +466,104 @@ " img_name=list_widget.value\n", " int_slider_widget.max = compute_k_max_color_images(img_name)\n", "list_widget.observe(update_k_max_color,'value')" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "code", - "execution_count": null, - "outputs": [], + "execution_count": 30, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "de4cb84649574a71b1f36d77a7f6e893", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='img_name', options=('cat', 'astro', 'coffee', 'koala', 'orange'), …" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "interact(print_matrices, img_name=list_widget, k=int_slider_widget)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "interact(compress_show_color_images_reshape,img_name=list_widget,k=int_slider_widget);" - ], + "execution_count": 31, "metadata": { - "collapsed": false, "pycharm": { "name": "#%%\n" } - } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "be1b04fae0ac4bd4916d48fc5b6eb7d6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='img_name', options=('cat', 'astro', 'coffee', 'koala', 'orange'), …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "interact(compress_show_color_images_reshape,img_name=list_widget,k=int_slider_widget);" + ] }, { "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, "source": [ - "### Layers method to compress color images\n", - "In the function ```compress_show_color_images_layer```, we treat a color image as a stack of 3 seperate two dimensional images (Red,blue and green layers) . We apply the truncated svd reconstruction on each two dimensional layer seperately.\n", + "### Metoda layers (warstwowa) do kompresji kolorowych obrazów\n", + "W funkcji ``compress_show_color_images_layer``, traktujemy kolorowy obraz jako stos 3 oddzielnych dwuwymiarowych obrazów (warstwy czerwona, niebieska i zielona). Stosujemy okrojoną rekonstrukcję svd na każdej dwuwymiarowej warstwie osobno.\n", "\n", - "```image_reconst_layers = [compress_svd(image[:,:,i],k)[0] for i in range(3)]```\n", + "``image_reconst_layers = [compress_svd(image[:,:,i],k)[0] for i in range(3)]``.\n", "\n", - "And we put back the reconstructed layers together.\n", + "I ponownie łączymy zrekonstruowane warstwy razem.\n", "\n", "```\n", "image_reconst = np.zeros(image.shape)\n", "for i in range(3):\n", - " image_reconst[:,:,i] = image_reconst_layers[i]\n", - "```\n", - "\n", - "\n", - " " - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } + " image_reconst[:,:,,i] = image_reconst_layers[i]\n", + "``` " + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "def compress_show_color_images_layer(img_name,k):\n", @@ -500,29 +581,27 @@ " compression_ratio =100.0*3* (k*(original_shape[0] + original_shape[1])+k)/(original_shape[0]*original_shape[1]*original_shape[2])\n", " plt.title(\"compression ratio={:.2f}\".format(compression_ratio)+\"%\")\n", " plt.imshow(image_reconst, vmin=0, vmax=255)\n" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + ] }, { "cell_type": "markdown", - "source": [ - "Here is the widget to explore layers method of compressing color images." - ], "metadata": { - "collapsed": false, "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "Oto widżet do badania metody layers (warstwowej) kompresji obrazów kolorowych." + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "def compute_k_max_color_images_layers(img_name):\n", @@ -537,27 +616,42 @@ " img_name=list_widget.value\n", " int_slider_widget.max = compute_k_max_color_images_layers(img_name)\n", "list_widget.observe(update_k_max_color_layers,'value')" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 34, "metadata": { - "collapsed": false, "pycharm": { "name": "#%%\n" } - } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "25bd4e6560f841569923087e4eae6e8c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(Dropdown(description='img_name', options=('cat', 'astro', 'coffee', 'koala', 'orange'), …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "interact(compress_show_color_images_layer,img_name=list_widget,k=int_slider_widget);" + ] }, { "cell_type": "code", "execution_count": null, + "metadata": {}, "outputs": [], - "source": [ - "interact(compress_show_color_images_layer,img_name=list_widget,k=int_slider_widget);" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } + "source": [] } ], "metadata": { @@ -576,9 +670,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.9" + "version": "3.8.13" } }, "nbformat": 4, "nbformat_minor": 1 -} \ No newline at end of file +}