├── README.md ├── dublin ├── airplane_1.jpg ├── airplane_2.jpg ├── airplane_3.jpg ├── airplane_4.jpg ├── airplane_5.jpg ├── airplane_6.jpg ├── airplane_7.jpg ├── bridge_1.jpg ├── bridge_2.jpg ├── bridge_3.jpg ├── convention_centre_1.jpg ├── convention_centre_2.jpg ├── convention_centre_3.jpg ├── convention_centre_4.jpg ├── convention_centre_5.jpg ├── river_1.jpg ├── river_2.jpg └── streets.jpg ├── poetry.lock ├── pyproject.toml ├── seagulls ├── seagull_1.jpeg ├── seagull_2.jpeg ├── seagull_3.jpeg ├── seagull_4.jpeg ├── seagull_5.jpeg └── seagull_6.jpeg └── src └── similarity.py /README.md: -------------------------------------------------------------------------------- 1 | # Finding similar photos 2 | 3 | [See the blog article.](https://mathspp.com/blog/finding-similar-photos) 4 | -------------------------------------------------------------------------------- /dublin/airplane_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/airplane_1.jpg -------------------------------------------------------------------------------- /dublin/airplane_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/airplane_2.jpg -------------------------------------------------------------------------------- /dublin/airplane_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/airplane_3.jpg -------------------------------------------------------------------------------- /dublin/airplane_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/airplane_4.jpg -------------------------------------------------------------------------------- /dublin/airplane_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/airplane_5.jpg -------------------------------------------------------------------------------- /dublin/airplane_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/airplane_6.jpg -------------------------------------------------------------------------------- /dublin/airplane_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/airplane_7.jpg -------------------------------------------------------------------------------- /dublin/bridge_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/bridge_1.jpg -------------------------------------------------------------------------------- /dublin/bridge_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/bridge_2.jpg -------------------------------------------------------------------------------- /dublin/bridge_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/bridge_3.jpg -------------------------------------------------------------------------------- /dublin/convention_centre_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/convention_centre_1.jpg -------------------------------------------------------------------------------- /dublin/convention_centre_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/convention_centre_2.jpg -------------------------------------------------------------------------------- /dublin/convention_centre_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/convention_centre_3.jpg -------------------------------------------------------------------------------- /dublin/convention_centre_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/convention_centre_4.jpg -------------------------------------------------------------------------------- /dublin/convention_centre_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/convention_centre_5.jpg -------------------------------------------------------------------------------- /dublin/river_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/river_1.jpg -------------------------------------------------------------------------------- /dublin/river_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/river_2.jpg -------------------------------------------------------------------------------- /dublin/streets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/dublin/streets.jpg -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "black" 3 | version = "22.12.0" 4 | description = "The uncompromising code formatter." 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=3.7" 8 | 9 | [package.dependencies] 10 | click = ">=8.0.0" 11 | mypy-extensions = ">=0.4.3" 12 | pathspec = ">=0.9.0" 13 | platformdirs = ">=2" 14 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 15 | 16 | [package.extras] 17 | colorama = ["colorama (>=0.4.3)"] 18 | d = ["aiohttp (>=3.7.4)"] 19 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 20 | uvloop = ["uvloop (>=0.15.2)"] 21 | 22 | [[package]] 23 | name = "click" 24 | version = "8.1.3" 25 | description = "Composable command line interface toolkit" 26 | category = "dev" 27 | optional = false 28 | python-versions = ">=3.7" 29 | 30 | [package.dependencies] 31 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 32 | 33 | [[package]] 34 | name = "colorama" 35 | version = "0.4.6" 36 | description = "Cross-platform colored terminal text." 37 | category = "dev" 38 | optional = false 39 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 40 | 41 | [[package]] 42 | name = "mypy-extensions" 43 | version = "0.4.3" 44 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 45 | category = "dev" 46 | optional = false 47 | python-versions = "*" 48 | 49 | [[package]] 50 | name = "pathspec" 51 | version = "0.11.0" 52 | description = "Utility library for gitignore style pattern matching of file paths." 53 | category = "dev" 54 | optional = false 55 | python-versions = ">=3.7" 56 | 57 | [[package]] 58 | name = "pillow" 59 | version = "9.4.0" 60 | description = "Python Imaging Library (Fork)" 61 | category = "main" 62 | optional = false 63 | python-versions = ">=3.7" 64 | 65 | [package.extras] 66 | docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] 67 | tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] 68 | 69 | [[package]] 70 | name = "platformdirs" 71 | version = "2.6.2" 72 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 73 | category = "dev" 74 | optional = false 75 | python-versions = ">=3.7" 76 | 77 | [package.extras] 78 | docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"] 79 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] 80 | 81 | [[package]] 82 | name = "tomli" 83 | version = "2.0.1" 84 | description = "A lil' TOML parser" 85 | category = "dev" 86 | optional = false 87 | python-versions = ">=3.7" 88 | 89 | [metadata] 90 | lock-version = "1.1" 91 | python-versions = "^3.10" 92 | content-hash = "1cc088def43040b017d718b088fbbfc0b6d9bbc2acfd31363304457623d513b1" 93 | 94 | [metadata.files] 95 | black = [ 96 | {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, 97 | {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, 98 | {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, 99 | {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, 100 | {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, 101 | {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, 102 | {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, 103 | {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, 104 | {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, 105 | {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, 106 | {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, 107 | {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, 108 | ] 109 | click = [ 110 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 111 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 112 | ] 113 | colorama = [ 114 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 115 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 116 | ] 117 | mypy-extensions = [ 118 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 119 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 120 | ] 121 | pathspec = [ 122 | {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, 123 | {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, 124 | ] 125 | pillow = [ 126 | {file = "Pillow-9.4.0-1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1"}, 127 | {file = "Pillow-9.4.0-1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12"}, 128 | {file = "Pillow-9.4.0-1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd"}, 129 | {file = "Pillow-9.4.0-1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9"}, 130 | {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"}, 131 | {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"}, 132 | {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"}, 133 | {file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"}, 134 | {file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"}, 135 | {file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"}, 136 | {file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"}, 137 | {file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"}, 138 | {file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"}, 139 | {file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"}, 140 | {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"}, 141 | {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"}, 142 | {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"}, 143 | {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d"}, 144 | {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57"}, 145 | {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5"}, 146 | {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070"}, 147 | {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28"}, 148 | {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35"}, 149 | {file = "Pillow-9.4.0-cp310-cp310-win32.whl", hash = "sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a"}, 150 | {file = "Pillow-9.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391"}, 151 | {file = "Pillow-9.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133"}, 152 | {file = "Pillow-9.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132"}, 153 | {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0"}, 154 | {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35"}, 155 | {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab"}, 156 | {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4"}, 157 | {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d"}, 158 | {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8"}, 159 | {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a"}, 160 | {file = "Pillow-9.4.0-cp311-cp311-win32.whl", hash = "sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c"}, 161 | {file = "Pillow-9.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee"}, 162 | {file = "Pillow-9.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493"}, 163 | {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327"}, 164 | {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe"}, 165 | {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57"}, 166 | {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4"}, 167 | {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5"}, 168 | {file = "Pillow-9.4.0-cp37-cp37m-win32.whl", hash = "sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e"}, 169 | {file = "Pillow-9.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6"}, 170 | {file = "Pillow-9.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9"}, 171 | {file = "Pillow-9.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011"}, 172 | {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df"}, 173 | {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837"}, 174 | {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b"}, 175 | {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d"}, 176 | {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b"}, 177 | {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f"}, 178 | {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628"}, 179 | {file = "Pillow-9.4.0-cp38-cp38-win32.whl", hash = "sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d"}, 180 | {file = "Pillow-9.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a"}, 181 | {file = "Pillow-9.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569"}, 182 | {file = "Pillow-9.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed"}, 183 | {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815"}, 184 | {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264"}, 185 | {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e"}, 186 | {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503"}, 187 | {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6"}, 188 | {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2"}, 189 | {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153"}, 190 | {file = "Pillow-9.4.0-cp39-cp39-win32.whl", hash = "sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c"}, 191 | {file = "Pillow-9.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b"}, 192 | {file = "Pillow-9.4.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5"}, 193 | {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286"}, 194 | {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd"}, 195 | {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df"}, 196 | {file = "Pillow-9.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336"}, 197 | {file = "Pillow-9.4.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3"}, 198 | {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa"}, 199 | {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb"}, 200 | {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a"}, 201 | {file = "Pillow-9.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9"}, 202 | {file = "Pillow-9.4.0.tar.gz", hash = "sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e"}, 203 | ] 204 | platformdirs = [ 205 | {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"}, 206 | {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"}, 207 | ] 208 | tomli = [ 209 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 210 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 211 | ] 212 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "image-similarity" 3 | version = "0.1.0" 4 | description = "Find similar images." 5 | authors = ["rodrigogiraoserrao <5621605+RodrigoGiraoSerrao@users.noreply.github.com>"] 6 | readme = "README.md" 7 | packages = [{include = "image_similarity"}] 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.10" 11 | Pillow = "^9.4.0" 12 | 13 | [tool.poetry.group.dev.dependencies] 14 | black = "^22.12.0" 15 | 16 | [build-system] 17 | requires = ["poetry-core"] 18 | build-backend = "poetry.core.masonry.api" 19 | -------------------------------------------------------------------------------- /seagulls/seagull_1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/seagulls/seagull_1.jpeg -------------------------------------------------------------------------------- /seagulls/seagull_2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/seagulls/seagull_2.jpeg -------------------------------------------------------------------------------- /seagulls/seagull_3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/seagulls/seagull_3.jpeg -------------------------------------------------------------------------------- /seagulls/seagull_4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/seagulls/seagull_4.jpeg -------------------------------------------------------------------------------- /seagulls/seagull_5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/seagulls/seagull_5.jpeg -------------------------------------------------------------------------------- /seagulls/seagull_6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathspp/Finding-similar-photos/f08202ef1f3d920998aa80090d1654f0370ae5c3/seagulls/seagull_6.jpeg -------------------------------------------------------------------------------- /src/similarity.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | from pathlib import Path 3 | 4 | from PIL import Image, ImageChops 5 | 6 | 7 | def summarise(img: Image.Image) -> Image.Image: 8 | """Summarise an image into a 16 x 16 image.""" 9 | resized = img.resize((16, 16)) 10 | return resized 11 | 12 | 13 | def difference(img1: Image.Image, img2: Image.Image) -> float: 14 | """Find the difference between two images.""" 15 | 16 | diff = ImageChops.difference(img1, img2) 17 | 18 | acc = 0 19 | width, height = diff.size 20 | for w, h in product(range(width), range(height)): 21 | r, g, b = diff.getpixel((w, h)) 22 | acc += (r + g + b) / 3 23 | 24 | average_diff = acc / (width * height) 25 | normalised_diff = average_diff / 255 26 | return normalised_diff 27 | 28 | 29 | def explore_directory(path: Path) -> None: 30 | """Find images in a directory and compare them all.""" 31 | 32 | files = ( 33 | list(path.glob("*.jpg")) + list(path.glob("*.jpeg")) + list(path.glob("*.png")) 34 | ) 35 | diffs = {} 36 | 37 | summaries = [(file, summarise(Image.open(file))) for file in files] 38 | 39 | for (f1, sum1), (f2, sum2) in product(summaries, repeat=2): 40 | key = tuple(sorted([str(f1), str(f2)])) 41 | if f1 == f2 or key in diffs: 42 | continue 43 | 44 | diff = difference(sum1, sum2) 45 | print(key, diff) 46 | diffs[key] = diff 47 | 48 | print() 49 | print("Near-duplicates found:") 50 | print("======================") 51 | for key, diff in diffs.items(): 52 | if diff < 0.07: 53 | print(key) 54 | 55 | 56 | if __name__ == "__main__": 57 | explore_directory(Path("dublin")) 58 | --------------------------------------------------------------------------------