{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## libvips\n\n[libvips](https://www.libvips.org/) is a demand-driven, horizontally threaded image processing library. Compared to similar libraries, libvips runs quickly and uses little memory. This notebook shows how you can install libvips and use it in an offline environment. [@analokamus](https://www.kaggle.com/analokamus) shared a similar [notebook](https://www.kaggle.com/code/analokamus/how-to-use-pyvips-offline) a year ago for [Mayo Clinic - STRIP AI](https://www.kaggle.com/competitions/mayo-clinic-strip-ai) competition but it is outdated and not working at the moment.","metadata":{}},{"cell_type":"markdown","source":"## Installation","metadata":{}},{"cell_type":"markdown","source":"Update package lists and download libvips and its dependencies.","metadata":{}},{"cell_type":"code","source":"!sudo apt-get update\n!sudo apt-get install libvips-dev -y --no-install-recommends --download-only -o dir::cache='./'","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-10-17T11:25:09.65014Z","iopub.execute_input":"2023-10-17T11:25:09.650486Z","iopub.status.idle":"2023-10-17T11:25:15.360419Z","shell.execute_reply.started":"2023-10-17T11:25:09.650459Z","shell.execute_reply":"2023-10-17T11:25:15.359256Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!mkdir ./libvips\n!mv ./archives/* ./libvips\n!rm -rf ./archives\n!ls ./libvips","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-10-17T11:25:15.362931Z","iopub.execute_input":"2023-10-17T11:25:15.363353Z","iopub.status.idle":"2023-10-17T11:25:19.213014Z","shell.execute_reply.started":"2023-10-17T11:25:15.363315Z","shell.execute_reply":"2023-10-17T11:25:19.211901Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Install libvips and its dependencies.","metadata":{}},{"cell_type":"code","source":"!yes | sudo dpkg -i ./libvips/*.deb","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-10-17T11:25:19.214994Z","iopub.execute_input":"2023-10-17T11:25:19.21538Z","iopub.status.idle":"2023-10-17T11:25:49.679267Z","shell.execute_reply.started":"2023-10-17T11:25:19.215331Z","shell.execute_reply":"2023-10-17T11:25:49.678137Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Install python bindings of libvips (pyvips) and wheel archive it.","metadata":{}},{"cell_type":"code","source":"!pip install pyvips\n!pip wheel pyvips\n!mkdir pyvips\n!mv *.whl ./pyvips","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-10-17T11:25:49.681967Z","iopub.execute_input":"2023-10-17T11:25:49.683035Z","iopub.status.idle":"2023-10-17T11:26:03.349973Z","shell.execute_reply.started":"2023-10-17T11:25:49.682993Z","shell.execute_reply":"2023-10-17T11:26:03.348689Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Getting Started","metadata":{}},{"cell_type":"code","source":"import os\nos.environ['OPENCV_IO_MAX_IMAGE_PIXELS'] = str(pow(2, 40))\n\nfrom pathlib import Path\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport pyvips\nimport matplotlib.pyplot as plt","metadata":{"execution":{"iopub.status.busy":"2023-10-17T11:26:03.351835Z","iopub.execute_input":"2023-10-17T11:26:03.353017Z","iopub.status.idle":"2023-10-17T11:26:03.359474Z","shell.execute_reply.started":"2023-10-17T11:26:03.352976Z","shell.execute_reply":"2023-10-17T11:26:03.358397Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Largest image in the training set is 51346.png and its size is 48363x84440.","metadata":{}},{"cell_type":"code","source":"competition_dataset_directory = Path('/kaggle/input/UBC-OCEAN')\n\ndf_train = pd.read_csv(competition_dataset_directory / 'train.csv')\nlargest_image_row = df_train.loc[np.argmax(df_train['image_height'] * df_train['image_width'])]\nlargest_image_row","metadata":{"execution":{"iopub.status.busy":"2023-10-17T11:26:03.361282Z","iopub.execute_input":"2023-10-17T11:26:03.361947Z","iopub.status.idle":"2023-10-17T11:26:03.383573Z","shell.execute_reply.started":"2023-10-17T11:26:03.361911Z","shell.execute_reply":"2023-10-17T11:26:03.38245Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_path = str(competition_dataset_directory / 'train_images' / f'{largest_image_row[\"image_id\"]}.png')\nimage_path","metadata":{"execution":{"iopub.status.busy":"2023-10-17T11:26:03.384875Z","iopub.execute_input":"2023-10-17T11:26:03.385992Z","iopub.status.idle":"2023-10-17T11:26:03.393285Z","shell.execute_reply.started":"2023-10-17T11:26:03.385955Z","shell.execute_reply":"2023-10-17T11:26:03.392046Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def resize_with_aspect_ratio(image, longest_edge):\n\n    \"\"\"\n    Resize image while preserving its aspect ratio\n\n    Parameters\n    ----------\n    image: numpy.ndarray of shape (height, width, 3)\n        Image array\n\n    longest_edge: int\n        Desired number of pixels on the longest edge\n\n    Returns\n    -------\n    image: numpy.ndarray of shape (resized_height, resized_width, 3)\n        Resized image array\n    \"\"\"\n\n    height, width = image.shape[:2]\n    scale = longest_edge / max(height, width)\n    image = cv2.resize(image, dsize=(int(np.ceil(width * scale)), int(np.ceil(height * scale))), interpolation=cv2.INTER_LANCZOS4)\n\n    return image\n\n\ndef vips_read_image(image_path, longest_edge):\n    \n    \"\"\"\n    Read image using libvips\n\n    Parameters\n    ----------\n    image_path: str\n        Path of the image\n\n    Returns\n    -------\n    image: numpy.ndarray of shape (height, width, 3)\n        Image array\n    \"\"\"\n    \n    image_thumbnail = pyvips.Image.thumbnail(image_path, longest_edge)\n\n    return np.ndarray(\n        buffer=image_thumbnail.write_to_memory(),\n        dtype=np.uint8,\n        shape=[image_thumbnail.height, image_thumbnail.width, image_thumbnail.bands]\n    )\n\n\ndef visualize_image(image, title, path=None):\n\n    \"\"\"\n    Visualize the given image\n\n    Parameters\n    ----------\n    image: numpy.ndarray of shape (height, width, channel)\n        Image array\n        \n    title: str\n        Title of the plot\n\n    path: str or None\n        Path of the output file or None (if path is None, plot is displayed with selected backend)\n    \"\"\"\n\n    fig, ax = plt.subplots(figsize=(8, 8))\n    ax.imshow(image)\n    ax.set_xlabel('')\n    ax.set_ylabel('')\n    ax.tick_params(axis='x', labelsize=15, pad=10)\n    ax.tick_params(axis='y', labelsize=15, pad=10)\n    ax.set_title(title, size=15, pad=12.5, loc='center', wrap=True)\n\n    if path is None:\n        plt.show()\n    else:\n        plt.savefig(path)\n        plt.close(fig)\n","metadata":{"execution":{"iopub.status.busy":"2023-10-17T11:26:43.188956Z","iopub.execute_input":"2023-10-17T11:26:43.19013Z","iopub.status.idle":"2023-10-17T11:26:43.2006Z","shell.execute_reply.started":"2023-10-17T11:26:43.190081Z","shell.execute_reply":"2023-10-17T11:26:43.199304Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Reading and resizing an image with pure OpenCV approach takes approximately **3** minutes and consumes **48363x84440x3x8** bits of RAM which is **12.25** gigabytes. Available RAM in Kaggle GPU notebooks isn't enough to load that image due to IO overhead so that code block is disabled in this version.","metadata":{}},{"cell_type":"code","source":"%%time\n\nis_gpu = True\n\nif is_gpu is False:\n    image = cv2.imread(image_path)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    image = resize_with_aspect_ratio(image=image, longest_edge=5000)\n\n    visualize_image(\n        image=image,\n        title=f'Image: {largest_image_row[\"image_id\"]} Label: {largest_image_row[\"label\"]}\\nHeight: {image.shape[0]} Width: {image.shape[1]}\\nMean: {np.mean(image):.2f} Std: {np.std(image):.2f}\\nMin: {np.min(image):.2f} Max: {np.max(image):.2f}'\n    )","metadata":{"execution":{"iopub.status.busy":"2023-10-17T11:26:45.083348Z","iopub.execute_input":"2023-10-17T11:26:45.083699Z","iopub.status.idle":"2023-10-17T11:26:45.091264Z","shell.execute_reply.started":"2023-10-17T11:26:45.083671Z","shell.execute_reply":"2023-10-17T11:26:45.090087Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Reading and resizing an image using libvips takes approximately **2.5** minutes and consumes significantly less RAM. libvips achieves this improvement by only keeping the pixels currently being processed in RAM and by having an efficient, threaded image IO system.","metadata":{}},{"cell_type":"code","source":"%%time\n\nimage = vips_read_image(image_path=image_path, longest_edge=5000)\n\nvisualize_image(\n    image=image,\n    title=f'Image: {largest_image_row[\"image_id\"]} Label: {largest_image_row[\"label\"]}\\nHeight: {image.shape[0]} Width: {image.shape[1]}\\nMean: {np.mean(image):.2f} Std: {np.std(image):.2f}\\nMin: {np.min(image):.2f} Max: {np.max(image):.2f}'\n)","metadata":{"execution":{"iopub.status.busy":"2023-10-17T11:26:45.719993Z","iopub.execute_input":"2023-10-17T11:26:45.721113Z","iopub.status.idle":"2023-10-17T11:29:07.262273Z","shell.execute_reply.started":"2023-10-17T11:26:45.721047Z","shell.execute_reply":"2023-10-17T11:29:07.261458Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"For further details refer to the [libvips](https://www.libvips.org/API/current/) and [pyvips](https://libvips.github.io/pyvips/) documentations.the","metadata":{}},{"cell_type":"markdown","source":"Output of this notebook can be used to install libvips and pyvips for offline submission. This version works with GPU P100 notebooks and the previous works with CPU notebooks.\n\n* Click \"Add Data\" on your own notebook\n* Search for \"libvips/pyvips Installation and Getting Started\" and add this notebook as a data source\n* Run those lines\n\n```shell\n!yes | sudo dpkg -i /kaggle/input/libvips-pyvips-installation-and-getting-started/libvips/*.deb\n!pip install /kaggle/input/libvips-pyvips-installation-and-getting-started/pyvips/pyvips-2.2.1-py2.py3-none-any.whl --no-index --find-links /kaggle/input/libvips-pyvips-installation-and-getting-started/pyvips\n```","metadata":{}}]}