{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# image processing\n", "### Average Hash\n", "- 이미지를 비교 가능한 해시 값으로 나타낸 것\n", "- 해시 함수 MD5, SHA256 등을 이용해 데이터 값을 간단한 해시 값으로 변환할 수 있음\n", "- 이미지가 비슷한지 등을 검출할 때는 해시함수를 사용하면 안됨. 해상도 크기 조정, 색조 보정, 압축 형식 변경 등으로 해시값이 달라짐" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2017-10-29T05:51:58.038255Z", "start_time": "2017-10-29T05:51:57.944823Z" }, "collapsed": true }, "outputs": [], "source": [ "from PIL import Image\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2017-10-29T05:53:17.470410Z", "start_time": "2017-10-29T05:53:17.460198Z" }, "collapsed": true }, "outputs": [], "source": [ "def average_hash(fname, size = 16):\n", " img = Image.open(fname)\n", " img = img.convert('L') # 1을 지정하면 이진화, RGB, RGBA, CMYK 등의 모드도 지원\n", " img = img.resize((size, size), Image.ANTIALIAS)\n", " pixel_data = img.getdata()\n", " pixels = np.array(pixel_data)\n", " pixels = pixels.reshape((size, size))\n", " avg = pixels.mean()\n", " diff = 1 * (pixels > avg)\n", " return diff" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2017-10-29T05:57:21.471308Z", "start_time": "2017-10-29T05:57:21.464335Z" }, "collapsed": true }, "outputs": [], "source": [ "def np2hash(ahash):\n", " bhash = []\n", " for nl in ahash.tolist():\n", " s1 = [str(i)for i in nl]\n", " s2 = ''.join(s1)\n", " i = int(s2, 2)\n", " bhash.append('%04x' % i)\n", " return ''.join(bhash)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2017-10-29T05:57:21.919154Z", "start_time": "2017-10-29T05:57:21.912923Z" } }, "outputs": [], "source": [ "ahash = average_hash('eiffel_tower.jpeg')" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2017-10-29T05:57:22.130937Z", "start_time": "2017-10-29T05:57:22.126482Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1]\n", " [0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1]\n", " [0 0 1 1 1 1 1 1 0 0 0 0 1 1 1 1]\n", " [0 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1]\n", " [0 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1]\n", " [1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1]\n", " [1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1]\n", " [1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1]\n", " [1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1]\n", " [1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1]\n", " [0 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1]\n", " [0 0 1 1 1 1 1 0 0 0 1 1 1 1 0 0]\n", " [0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0]\n", " [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0]\n", " [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]\n", " [0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0]]\n" ] } ], "source": [ "print(ahash)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2017-10-29T05:57:22.682635Z", "start_time": "2017-10-29T05:57:22.679094Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7f813f033f0f7f0f7f1ffe7ffe7fff7ffe7fff7f7e3f3e3c0438008000000200\n" ] } ], "source": [ "print(np2hash(ahash))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Caltech 101 데이터\n", "- 해밍 거리 : 같은 문자수를 가진 2개의 문자열에서 대응하는 위치에 있는 문자 중 다른 것의 개수\n", " - 256글자의 해시값 중 얼마나 다른지 찾고 이 기반으로 이미지 차이를 구분" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2017-10-29T06:05:46.326719Z", "start_time": "2017-10-29T06:05:46.324248Z" }, "collapsed": true }, "outputs": [], "source": [ "import os, re" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "ExecuteTime": { "end_time": "2017-10-29T06:20:49.952048Z", "start_time": "2017-10-29T06:20:08.396686Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.0 > ./image/101_ObjectCategories/chair/image_0016.jpg\n", "0.22265625 > ./image/101_ObjectCategories/stop_sign/image_0019.jpg\n", "0.2265625 > ./image/101_ObjectCategories/chair/image_0031.jpg\n", "0.23046875 > ./image/101_ObjectCategories/airplanes/image_0129.jpg\n", "0.234375 > ./image/101_ObjectCategories/umbrella/image_0009.jpg\n", "0.23828125 > ./image/101_ObjectCategories/airplanes/image_0124.jpg\n", "0.24609375 > ./image/101_ObjectCategories/chair/image_0001.jpg\n", "0.24609375 > ./image/101_ObjectCategories/chair/image_0002.jpg\n", "0.24609375 > ./image/101_ObjectCategories/dragonfly/image_0001.jpg\n", "ok\n" ] } ], "source": [ "search_dir = \"./image/101_ObjectCategories/\"\n", "cache_dir = \"./image/cache_avhash\"\n", "\n", "if not os.path.exists(cache_dir):\n", " os.mkdir(cache_dir)\n", " \n", "def average_hash(fname, size = 16):\n", " fname2 = fname[len(search_dir):]\n", " # image cache\n", " \n", " cache_file = cache_dir + \"/\" + fname2.replace('/','_') + '.csv'\n", " if not os.path.exists(cache_file):\n", " img = Image.open(fname)\n", " img = img.convert('L').resize((size, size), Image.ANTIALIAS)\n", " pixels = np.array(img.getdata()).reshape((size,size))\n", " avg = pixels.mean()\n", " px = 1 * (pixels > avg)\n", " np.savetxt(cache_file, px, fmt=\"%.0f\", delimiter=\",\")\n", " else:\n", " px = np.loadtxt(cache_file, delimiter=\",\")\n", " return px\n", "\n", "def hamming_dist(a, b):\n", " aa = a.reshape(1, -1)\n", " ab = b.reshape(1, -1)\n", " dist = (aa != ab).sum()\n", " return dist\n", "\n", "def enum_all_files(path):\n", " for root, dirs, files in os.walk(path):\n", " for f in files:\n", " fname = os.path.join(root, f)\n", " if re.search(r'\\.(jpg|jpeg|pnp)$', fname):\n", " yield fname\n", " \n", "def find_image(fname, rate):\n", " src = average_hash(fname)\n", " for fname in enum_all_files(search_dir):\n", " dst = average_hash(fname)\n", " diff_r = hamming_dist(src, dst) / 256\n", " if diff_r < rate:\n", " yield (diff_r, fname)\n", " \n", "srcfile = search_dir + \"/chair/image_0016.jpg\"\n", "html = \"\"\n", "sim = list(find_image(srcfile, 0.25))\n", "sim = sorted(sim, key = lambda x: x[0])\n", "for r, f in sim:\n", " print(r, \">\", f)\n", " s = '

[ 차이 : ' + str(r) + '-' + os.path.basename(f) + ']

' + \\\n", " '

' + '

'\n", " html += s\n", "\n", "html = \"\"\"/head>\n", "

원래 이미지

\n", "

{1}\"\"\".format(srcfile, html)\n", "with open(\"./avgash-search-output.html\", \"w\", encoding=\"utf-8\") as f:\n", " f.write(html)\n", "print(\"ok\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## CNN\n", "- 일정한 크기로 리사이즈한 후, 24비트 RGB 형식으로 변환 -> Numpy 배열로 저장" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from PIL import Image\n", "import os, glob\n", "import numpy as np\n", "from sklearn.model_selection import train_test_split\n", "\n", "# 분류 대상 카테고리 선택\n", "caltech_dir" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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", "version": "3.6.1" }, "latex_envs": { "LaTeX_envs_menu_present": true, "autocomplete": true, "bibliofile": "biblio.bib", "cite_by": "apalike", "current_citInitial": 1, "eqLabelWithNumbers": true, "eqNumInitial": 1, "hotkeys": { "equation": "Ctrl-E", "itemize": "Ctrl-I" }, "labels_anchors": false, "latex_user_defs": false, "report_style_numbering": false, "user_envs_cfg": false }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }