{"metadata": {"language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "mimetype": "text/x-python", "name": "python", "pygments_lexer": "ipython3", "file_extension": ".py", "nbconvert_exporter": "python", "version": "3.6.3"}, "kernelspec": {"language": "python", "name": "python3", "display_name": "Python 3"}}, "nbformat": 4, "nbformat_minor": 1, "cells": [{"metadata": {"_cell_guid": "fe6837af-968d-4153-990f-3b36765f55bc", "_uuid": "c043b438a55a9e789fdd74ce6aa3df21be9490eb"}, "source": ["# Fast benchmark: Pillow vs OpenCV\n", "\n", "*Background: when we deal with images in image-based problems and deploy a deep learning solution, it is better to have a fast image reading and transforming library. Let's compare Pillow and OpenCV python libraries on image loading and some basic transformations on source images from Carvana competition.*\n", "\n", "[OpenCV](https://github.com/opencv/opencv): C++, python-wrapper\n", "\n", "[Pillow](https://github.com/python-pillow/Pillow): Python, C\n", "\n", "`\n", "`\n", "\n", "Intuition says that Opencv should be a little faster, let's see this by examples\n", "\n", "`\n", "`\n", "\n", "*This question I asked myself after reading the PyTorch [documentation on image transformation](http://pytorch.org/docs/0.2.0/_modules/torchvision/transforms.html). Most of transformations take as input a PIL image.*\n"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "8147c7e9-5d88-46d6-a6b7-cf85d8f8fd05", "_uuid": "302855e43533de243af523aaa6e9dfd1a6461319", "collapsed": true}, "source": ["import PIL\n", "import cv2"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "b9c0daa3-fb76-4c38-8f44-b64197a3c4fc", "_uuid": "14d4e6e2ed7f1090385e26cfd04c8796fafc74fe"}, "source": ["At first, let's get packages versions, specs and some info on the machine"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "c2363288-f996-4445-877d-7e5357e2971c", "_uuid": "f8e881a0591118816970c6fe633eaab55ab9fd7f"}, "source": ["print(cv2.__version__, cv2.__spec__)\n", "print(cv2.getBuildInformation())"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "95e124b5-2c97-4d23-b0a5-0265b730c1aa", "_uuid": "550681ecb1c3236816a95fd40d58bd7deaddc4fc"}, "source": ["PIL.__version__, PIL.__spec__"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "9dcd6558-e1cc-45fd-aad8-c7654bdb017b", "_uuid": "9772ac75e3fe345885e8160f2ec616f24768cd38", "collapsed": true}, "source": ["!cat /proc/cpuinfo | egrep \"model name\""], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "eba30b38-58a6-4599-955e-7ffdde7d5048", "_uuid": "650350164b5863b1a2f3adab27f9cb19fef33341"}, "source": ["Data storage info: `ROTA 1` means rotational device"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "aa073f57-b5de-4423-b13c-4424951d60b9", "_uuid": "0adda47f72406997368fe72790eb49007b84cf79", "collapsed": true}, "source": ["!lsblk -o name,rota,type,mountpoint"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "ed34bcb0-1d98-4a38-a991-cf0dbe97621a", "_uuid": "76face6ed26c085584c53445e2a93d12fc4f2ed6"}, "source": ["Now let's setup the input data"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "0674883d-3e89-4e72-b49f-202c61091cfe", "_uuid": "d0c55dda3dd0e6785403ea11ee8c3fa6e0db6ebd"}, "source": ["import os\n", "this_path = os.path.dirname('.')\n", "\n", "INPUT_PATH = os.path.abspath(os.path.join(this_path, '..', 'input'))\n", "TRAIN_DATA = os.path.join(INPUT_PATH, \"train\")\n", "from glob import glob\n", "filenames = glob(os.path.join(TRAIN_DATA, \"*.jpg\"))\n", "len(filenames)"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "3a552595-4091-451e-8c65-ecf3d1e72677", "_uuid": "7c0d218540603c08239f247e38c7e3a3509bb3de", "collapsed": true}, "source": ["import matplotlib.pylab as plt\n", "%matplotlib inline"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "d0236dcd-7807-49d7-a929-27ef1ff2e73f", "_uuid": "b7c8248d5dcc2f4aec9d4a5823021a68b15b955e"}, "source": ["## 1 stage: 100 images, load image + blur + flip"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "0c26f22f-e895-4bd5-86c1-a1b85583ac82", "_uuid": "1f483b02a9800b54a2def10e1ce30cce28219e15", "collapsed": true}, "source": ["import numpy as np\n", "from PIL import Image, ImageOps\n", "\n", "def stage_1_PIL(filename):\n", "    img_pil = Image.open(filename)\n", "    img_pil = ImageOps.box_blur(img_pil, radius=1)\n", "    img_pil = img_pil.transpose(Image.FLIP_LEFT_RIGHT)\n", "    return np.asarray(img_pil)\n", "\n", "def stage_1_cv2(filename):\n", "    img = cv2.imread(filename)\n", "    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n", "    img = cv2.blur(img, ksize=(3, 3))\n", "    img = cv2.flip(img, flipCode=1)\n", "    return img"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "5343a843-8186-4400-a323-3b2298153cc6", "_uuid": "b398a9c0d768d7b908a490585fc90f1900749a13"}, "source": ["Let's compare briefly results of transformations on the first image. Results are not perfectly the same, but it is not important for the benchmark  "], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "df805da8-62e2-4cd9-a13f-33c5c93e0917", "_uuid": "47ddadc3a89c6f7c9a84862c0e95fc92ada2b76d"}, "source": ["f = filenames[0]\n", "r1 = stage_1_PIL(f) \n", "r2 = stage_1_cv2(f)\n", "\n", "plt.figure(figsize=(16, 16))\n", "plt.subplot(131)\n", "plt.imshow(r1)\n", "plt.subplot(132)\n", "plt.imshow(r2)\n", "plt.subplot(133)\n", "plt.imshow(np.abs(r1 - r2))"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "e1895aa0-db81-4acb-8ed2-2b218e3f1c5d", "_uuid": "69928316afdeac5b21359ffe776d4390ea29c249"}, "source": ["%timeit -n5 -r3 [stage_1_PIL(f) for f in filenames[:100]]"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "f09d8ba9-3f6d-40cc-a1c3-b3da8b17db82", "_uuid": "ee161fb1bd1602577be77acc29c0d3de5eaaa078"}, "source": ["%timeit -n5 -r3 [stage_1_cv2(f) for f in filenames[:100]]"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "0979e00b-ad19-4f46-82a8-ba2315fa6ef4", "_uuid": "55dd6e637a42eee7c3f7be1a2f709e9f1c0c1bd9"}, "source": ["## 1b stage: 100 images, blur + flip"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "7ca206ff-a7d8-4df7-8e04-37b1a0396caa", "_uuid": "866e25c473efeb705eec2122a12f3abf5194e912", "collapsed": true}, "source": ["def stage_1b_PIL(img_pil):\n", "    img_pil = ImageOps.box_blur(img_pil, radius=1)\n", "    img_pil = img_pil.transpose(Image.FLIP_LEFT_RIGHT)\n", "    return np.asarray(img_pil)\n", "\n", "def stage_1b_cv2(img):    \n", "    img = cv2.blur(img, ksize=(3, 3))\n", "    img = cv2.flip(img, flipCode=1)\n", "    return img"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "5cff4af6-f2eb-4b66-b039-b3b36cfbae57", "_uuid": "9cd42bc801180e69592a238993dd6cc9a7c6eec8", "collapsed": true}, "source": ["imgs_PIL = [Image.open(filename) for filename in filenames[:100]]"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "90abab28-3817-4cd4-9a82-c10bcace758f", "_uuid": "10f401e1c8f3468b9557d872ba4ebd09c602cc41", "collapsed": true}, "source": ["def cv2_open(filename):\n", "    img = cv2.imread(filename)\n", "    return cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n", "\n", "imgs_cv2 = [cv2_open(filename) for filename in filenames[:100]]"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"scrolled": true, "_cell_guid": "ffefd74b-fb91-49b9-b7c6-1fc4bf52dc73", "_uuid": "bd6a1fe58e674f8a7aea775531455b8cc2e5ab67", "collapsed": true}, "source": ["%timeit -n5 -r3 [stage_1b_PIL(img_pil) for img_pil in imgs_PIL]"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "c73e8d8a-4874-42e7-a600-7949019c2d7e", "_uuid": "c3075512c7d4494e68a8f1002c40170f44f21da5", "collapsed": true}, "source": ["%timeit -n5 -r3 [stage_1b_cv2(img) for img in imgs_cv2]"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "7a9366e9-4bfb-4a28-b69e-42ede1ad6355", "_uuid": "41150c7b15d793e7f00ce66e2e4f3b8e5f62b233"}, "source": ["## 2 stage: 500 images, load image + resize + 2 flips"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "d2859361-fec6-4131-a549-32d154219ea8", "_uuid": "4a446a5aceee4c8c3d8701226075a798edc103c8", "collapsed": true}, "source": ["import numpy as np\n", "from PIL import Image, ImageOps\n", "\n", "\n", "def stage_2_PIL(filename):\n", "    img_pil = Image.open(filename)\n", "    img_pil = img_pil.resize((512, 512), Image.CUBIC)\n", "    img_pil = img_pil.transpose(Image.FLIP_LEFT_RIGHT)\n", "    img_pil = img_pil.transpose(Image.FLIP_TOP_BOTTOM)\n", "    return np.asarray(img_pil)\n", "\n", "def stage_2_cv2(filename):\n", "    img = cv2.imread(filename)\n", "    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)    \n", "    img = cv2.resize(img, dsize=(512, 512), interpolation=cv2.INTER_CUBIC)\n", "    img = cv2.flip(img, flipCode=1)\n", "    img = cv2.flip(img, flipCode=0)\n", "    return img"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "746878fa-298e-4a35-a3bd-19ccc0cd5c6a", "_uuid": "0947ead8055720f2468fda1a249f5a4ad322265e"}, "source": ["Again let's compare briefly results of transformations on the first image:"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "a2b3d205-3a41-4870-abd7-fa853dd2b12e", "_uuid": "c533a191e91ba66d4d64c28fccdb1a0b6e3264ba", "collapsed": true}, "source": ["f = filenames[0]\n", "r1 = stage_2_PIL(f) \n", "r2 = stage_2_cv2(f)\n", "\n", "plt.figure(figsize=(16, 16))\n", "plt.subplot(131)\n", "plt.imshow(r1)\n", "plt.subplot(132)\n", "plt.imshow(r2)\n", "plt.subplot(133)\n", "plt.imshow(np.abs(r1 - r2))"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "bec7dfa2-0625-4a54-811d-75945c5ef2ca", "_uuid": "b2a1628e5277819323f459ea08f01d6b45fc19de", "collapsed": true}, "source": ["%timeit -n5 -r3 [stage_2_PIL(f) for f in filenames[:200]]"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "c569bf0a-87d1-4809-bf89-29f5c4237287", "_uuid": "5625c60e74365aac58cace6b9394f6342590f84d", "collapsed": true}, "source": ["%timeit -n5 -r3 [stage_2_cv2(f) for f in filenames[:200]]"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "6cca2d73-8be7-432c-95a3-fea9bd8d84dc", "_uuid": "5edded39d8f9514e1f7aa9fd234ba2d320fd96da", "collapsed": true}, "source": [], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "92ab8207-e2a0-462f-94f0-9a0e20dacddd", "_uuid": "a664fbc231befdb9864bfc5b2bc6c5953e8276bd"}, "source": [], "cell_type": "markdown"}]}