├── .gitignore ├── README.md ├── config.yaml ├── deploy └── supervisor.conf ├── docs ├── Makefile ├── make.bat └── source │ ├── conf.py │ ├── develop │ └── main.rst │ ├── index.rst │ └── start │ └── main.rst ├── requirements.txt ├── runtests.py ├── server.py ├── src ├── __init__.py ├── core │ ├── __init__.py │ └── app.py ├── detector.py ├── detectors │ ├── Base.py │ ├── __init__.py │ ├── color.py │ ├── correlate.py │ ├── mse.py │ └── phash.py ├── handler │ ├── __init__.py │ ├── bindex.py │ ├── compare.py │ ├── demo.py │ └── home.py ├── lib │ ├── __init__.py │ ├── data.py │ └── image.py ├── routes.py ├── service │ ├── __init__.py │ ├── feature.py │ ├── manage.py │ └── match.py └── template │ ├── demo.html │ ├── index.html │ ├── nav.html │ ├── search.html │ ├── search_color_result.html │ ├── search_result.html │ ├── search_test.html │ └── search_test_upload.html └── tests ├── __init__.py ├── brick.png ├── find.png ├── finds_the_coin.py ├── img ├── 1.jpg ├── 10.jpg ├── 11.jpg ├── 14.jpg ├── 15.jpg ├── 16.jpg ├── 17.jpg ├── 18.jpg ├── 19.jpg ├── 21.jpg ├── 22.jpg ├── 23.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── 7.jpg ├── 8.jpg └── 9.jpg ├── imgs ├── img1 │ ├── 10.jpg │ ├── 11.jpg │ ├── 12.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── 7.jpg │ ├── 8.jpg │ ├── 9.jpg │ └── bd_logo1.png ├── img2 │ ├── jp_gates_contrast.png │ ├── jp_gates_original.png │ ├── jp_gates_photoshopped.png │ ├── u=1154773526,535753407&fm=21&gp=0.jpg │ ├── u=2382192293,4052630182&fm=21&gp=0.jpg │ ├── u=2473462771,2519378688&fm=21&gp=0.jpg │ ├── u=2476847967,542236518&fm=21&gp=0-2.jpg │ ├── u=2476847967,542236518&fm=21&gp=0.jpg │ ├── u=2741110226,162889272&fm=21&gp=0.jpg │ ├── u=2755629579,2099783568&fm=21&gp=0.jpg │ └── u=3598758,4045583727&fm=21&gp=0.jpg ├── img3 │ ├── 1.jpg │ └── 2.jpg ├── img4 │ ├── 1.jpg │ ├── 10.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 562c11dfa9ec8a13836f2676f103918fa0ecc02b.jpg │ ├── 738b4710b912c8fc1aae2af7fb039245d68821af-2.jpg │ ├── 738b4710b912c8fc1aae2af7fb039245d68821af.jpg │ ├── 9b46f21fbe096b630a632fc60a338744ebf8ac2a.jpg │ └── a8014c086e061d953a36eb027cf40ad162d9ca9c.jpg ├── imgdb │ └── .gitkeep ├── storage │ ├── .gitkeep │ ├── 0079d7d650f8833d4d152381aa397a49.png │ ├── 023808d2c8d421e59b44adf9d0975513.png │ ├── 160a31eb7ccb105f0ab9d5b7aede085b.png │ ├── 1d7f93bf15288d660ea283ad3ad2cf36.png │ ├── 2cb2bca1f460957b0de2e5fdd600437a.png │ ├── 3203a0be00ed57feb72a592dddf76c3e.png │ ├── 3ac2c13d9a48c75a703689b3c3780e61.png │ ├── 3cf2c674b35839f1a7801ab5b11448c5.png │ ├── 3e44218e97dd14e2efeac90d012ca00c.png │ ├── 4991385c86dadab8cf075e35949b6719.png │ ├── 4a71dae033504b97bc7bbe06f0fa447f.png │ ├── 4de571fcf072849f23ef19ef4f6b9738.png │ ├── 51ffa23048d553e5f633f177aef3c990.png │ ├── 5f9659b5ee80cde874970ed6aa10d109.png │ ├── 711be1f660213bdc5eceff97ec1f3d21.png │ ├── 7d5627485e8f4ee30d8fdf8d659282c6.png │ ├── a27a1c9c847a7534ebc590de56e037a8.png │ ├── af3727c3075c61f1bfa434b33f01a0ed.png │ ├── b714e06d15c85867bc785d36a8fe83d9.png │ ├── c1635f9e09bb7dcf76a6b04a51bae240.png │ ├── d266e6072469ca6103c5ace4bf77b6d7.png │ ├── eb30bf991b24eb14e6083390c29bb1da.png │ ├── ec86dff70f3634aff8a93f167f594a36.png │ └── ecc73686b4340c8a4fa5152221053705.png ├── storagetemp.jpg └── storagetemp.png ├── imgtemp.jpg ├── opencv.py ├── orgin_brick.jpg ├── origin_material.jpg ├── res.png ├── stone_ streamline.jpg ├── testOne.py ├── testThree.py ├── testTwo.py ├── test_base.py ├── test_camera.py ├── test_index.py ├── test_interceptors.py ├── test_queue.py └── test_time.py /.gitignore: -------------------------------------------------------------------------------- 1 | # gitignore for most project 2 | # 3 | # Created: 2015-7 4 | # Updated: 5 | # Maintainer: call@woola.net 6 | 7 | # ----------------------------- 8 | 9 | # Numerous always-ignore extensions 10 | 11 | *.diff 12 | *.err 13 | *.orig 14 | *.log 15 | *.rej 16 | *.swo 17 | *.swp 18 | *.vi 19 | 20 | ### SVN ### 21 | .svn/ 22 | 23 | 24 | ### Mercurial ### 25 | /.hg/* 26 | */.hg/* 27 | .hgignore 28 | 29 | 30 | ### CVS ### 31 | /CVS/* 32 | */CVS/* 33 | .cvsignore 34 | */.cvsignore 35 | 36 | ### Archives ### 37 | # It's better to unpack these files and commit the raw source because 38 | # git has its own built in compression methods. 39 | *.7z 40 | *.jar 41 | *.rar 42 | *.zip 43 | *.gz 44 | *.bzip 45 | *.bz2 46 | *.xz 47 | *.lzma 48 | *.cab 49 | 50 | #packing-only formats 51 | *.iso 52 | *.tar 53 | 54 | #package management formats 55 | *.dmg 56 | *.xpi 57 | *.gem 58 | *.egg 59 | *.deb 60 | *.rpm 61 | *.msi 62 | *.msm 63 | *.msp 64 | 65 | # ----------------------------- 66 | 67 | # OS or Editor 68 | 69 | ### OSX ### 70 | .DS_Store 71 | .AppleDouble 72 | .LSOverride 73 | 74 | # Icon must end with two \r 75 | Icon 76 | 77 | 78 | # Thumbnails 79 | ._* 80 | 81 | # Files that might appear on external disk 82 | .Spotlight-V100 83 | .Trashes 84 | 85 | # Directories potentially created on remote AFP share 86 | .AppleDB 87 | .AppleDesktop 88 | Network Trash Folder 89 | Temporary Items 90 | .apdisk 91 | 92 | 93 | ### Linux ### 94 | *~ 95 | 96 | # KDE directory preferences 97 | .directory 98 | 99 | 100 | ### Windows ### 101 | # Windows image file caches 102 | Thumbs.db 103 | ehthumbs.db 104 | 105 | # Folder config file 106 | Desktop.ini 107 | 108 | # Recycle Bin used on file shares 109 | $RECYCLE.BIN/ 110 | 111 | # Windows shortcuts 112 | *.lnk 113 | 114 | # ----------------------------- 115 | 116 | ### JetBrains ### 117 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 118 | 119 | ## Directory-based project format: 120 | .idea/ 121 | # if you remove the above rule, at least ignore the following: 122 | 123 | # User-specific stuff: 124 | # .idea/workspace.xml 125 | # .idea/tasks.xml 126 | # .idea/dictionaries 127 | 128 | # Sensitive or high-churn files: 129 | # .idea/dataSources.ids 130 | # .idea/dataSources.xml 131 | # .idea/sqlDataSources.xml 132 | # .idea/dynamic.xml 133 | # .idea/uiDesigner.xml 134 | 135 | # Gradle: 136 | # .idea/gradle.xml 137 | # .idea/libraries 138 | 139 | # Mongo Explorer plugin: 140 | # .idea/mongoSettings.xml 141 | 142 | ## File-based project format: 143 | *.ipr 144 | *.iws 145 | 146 | ## Plugin-specific files: 147 | 148 | # IntelliJ 149 | out/ 150 | 151 | # mpeltonen/sbt-idea plugin 152 | .idea_modules/ 153 | 154 | # JIRA plugin 155 | atlassian-ide-plugin.xml 156 | 157 | # Crashlytics plugin (for Android Studio and IntelliJ) 158 | com_crashlytics_export_strings.xml 159 | crashlytics.properties 160 | crashlytics-build.properties 161 | 162 | 163 | ### Eclipse ### 164 | *.pydevproject 165 | .metadata 166 | .gradle 167 | tmp/ 168 | *.tmp 169 | *.bak 170 | *~.nib 171 | local.properties 172 | .settings/ 173 | .loadpath 174 | 175 | # External tool builders 176 | .externalToolBuilders/ 177 | 178 | # Locally stored "Eclipse launch configurations" 179 | *.launch 180 | 181 | # CDT-specific 182 | .cproject 183 | 184 | # PDT-specific 185 | .buildpath 186 | 187 | # sbteclipse plugin 188 | .target 189 | 190 | # TeXlipse plugin 191 | .texlipse 192 | 193 | 194 | ### SublimeText ### 195 | # cache files for sublime text 196 | *.tmlanguage.cache 197 | *.tmPreferences.cache 198 | *.stTheme.cache 199 | 200 | # workspace files are user-specific 201 | *.sublime-workspace 202 | 203 | # project files should be checked into the repository, unless a significant 204 | # proportion of contributors will probably not be using SublimeText 205 | *.sublime-project 206 | 207 | # sftp configuration file 208 | sftp-config.json 209 | 210 | 211 | ### Dreamweaver ### 212 | # DW Dreamweaver added files 213 | _notes 214 | dwsync.xml 215 | 216 | 217 | ### vim ### 218 | [._]*.s[a-w][a-z] 219 | [._]s[a-w][a-z] 220 | *.un~ 221 | Session.vim 222 | .netrwhist 223 | 224 | # ----------------------------- 225 | 226 | # PHP 227 | 228 | ### Composer ### 229 | composer.phar 230 | vendor/ 231 | 232 | # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file 233 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file 234 | # composer.lock 235 | 236 | 237 | ### Symfony ### 238 | # Cache and logs (Symfony2) 239 | /app/cache/* 240 | /app/logs/* 241 | !app/cache/.gitkeep 242 | !app/logs/.gitkeep 243 | 244 | # Cache and logs (Symfony3) 245 | /var/cache/* 246 | /var/logs/* 247 | !var/cache/.gitkeep 248 | !var/logs/.gitkeep 249 | 250 | # Parameters 251 | /app/config/parameters.yml 252 | /app/config/parameters.ini 253 | 254 | # Managed by Composer 255 | /app/bootstrap.php.cache 256 | /var/bootstrap.php.cache 257 | /bin/* 258 | !bin/console 259 | !bin/symfony_requirements 260 | 261 | # Assets and user uploads 262 | /web/bundles/ 263 | /web/uploads/ 264 | 265 | # Build data 266 | /build/ 267 | 268 | # Composer PHAR 269 | /composer.phar 270 | 271 | 272 | ### Laravel ### 273 | /bootstrap/compiled.php 274 | .env.*.php 275 | .env.php 276 | 277 | 278 | ### Sass ### 279 | .sass-cache 280 | *.css.map 281 | 282 | # ----------------------------- 283 | 284 | ### python ### 285 | *.pyc 286 | tests/tmp/* 287 | ### docs ### 288 | /docs/build/* 289 | 290 | /indexdb 291 | /img/tmp/* 292 | !/img/tmp/.gitkeep 293 | /img/storage/* 294 | !/img/storage/.gitkeep 295 | *.db 296 | 297 | /venv 298 | /test/img/* 299 | *#* 300 | *~ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pictureCompare python 识图服务(类似于google识图搜索) 2 | ============================================= 3 | 4 | ### python 的第三方识图服务 5 | 6 | ```bash 7 | $ virtualenv venv 8 | $ source venv/bin/activate 9 | $ pip install -r requirements.txt 10 | $ python server.py 11 | ``` 12 | 13 | 安装配置 redis (使用redis 作为指纹缓存数据) 14 | 15 | 16 | 配置 config.yaml 17 | 18 | ```yaml 19 | redis: 20 | host: 192.168.10.10 21 | port: 6379 22 | db: 1 23 | 24 | ``` 25 | 26 | 开始使用: 27 | 28 | ```bash 29 | $ python server.py 30 | ``` 31 | 32 | 33 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | name: Picture Compare 2 | project_dir: None 3 | static_dir: img/ 4 | 5 | port: 5555 6 | binding_host: localhost 7 | # test 8 | image_package: [img1, img2, img3 ,img4] 9 | result_size: 20 10 | redis: 11 | host: 192.168.10.10 12 | port: 6379 13 | db: 1 14 | -------------------------------------------------------------------------------- /deploy/supervisor.conf: -------------------------------------------------------------------------------- 1 | [program:prod-dmc] 2 | command = sudo python server.py 3 | directory = /mnt/picture-compare/ 4 | process_name = %(program_name)s_%(process_num)s 5 | numprocs = 1 6 | autostart = true 7 | autorestart = true 8 | stdout_logfile = /mnt/picture-compare/access.log 9 | stdout_logfile_maxbytes = 10MB 10 | stderr_logfile = /mnt/picture-compare/error.log 11 | stderr_logfile_maxbytes = 10MB 12 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pictureCompare.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pictureCompare.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/pictureCompare" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pictureCompare" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 2> nul 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pictureCompare.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pictureCompare.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # pictureCompare documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Jul 14 09:35:20 2015. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import shlex 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [ 33 | 'sphinx.ext.autodoc', 34 | 'sphinx.ext.doctest', 35 | 'sphinx.ext.intersphinx', 36 | 'sphinx.ext.todo', 37 | 'sphinx.ext.coverage', 38 | 'sphinx.ext.mathjax', 39 | 'sphinx.ext.ifconfig', 40 | 'sphinx.ext.viewcode', 41 | ] 42 | 43 | # Add any paths that contain templates here, relative to this directory. 44 | templates_path = ['_templates'] 45 | 46 | # The suffix(es) of source filenames. 47 | # You can specify multiple suffix as a list of string: 48 | # source_suffix = ['.rst', '.md'] 49 | source_suffix = '.rst' 50 | 51 | # The encoding of source files. 52 | #source_encoding = 'utf-8-sig' 53 | 54 | # The master toctree document. 55 | master_doc = 'index' 56 | 57 | # General information about the project. 58 | project = u'pictureCompare' 59 | copyright = u'2015, Neo' 60 | author = u'Neo' 61 | 62 | # The version info for the project you're documenting, acts as replacement for 63 | # |version| and |release|, also used in various other places throughout the 64 | # built documents. 65 | # 66 | # The short X.Y version. 67 | version = '0.1.0' 68 | # The full version, including alpha/beta/rc tags. 69 | release = '0.1.0' 70 | 71 | # The language for content autogenerated by Sphinx. Refer to documentation 72 | # for a list of supported languages. 73 | # 74 | # This is also used if you do content translation via gettext catalogs. 75 | # Usually you set "language" from the command line for these cases. 76 | language = None 77 | 78 | # There are two options for replacing |today|: either, you set today to some 79 | # non-false value, then it is used: 80 | #today = '' 81 | # Else, today_fmt is used as the format for a strftime call. 82 | #today_fmt = '%B %d, %Y' 83 | 84 | # List of patterns, relative to source directory, that match files and 85 | # directories to ignore when looking for source files. 86 | exclude_patterns = [] 87 | 88 | # The reST default role (used for this markup: `text`) to use for all 89 | # documents. 90 | #default_role = None 91 | 92 | # If true, '()' will be appended to :func: etc. cross-reference text. 93 | #add_function_parentheses = True 94 | 95 | # If true, the current module name will be prepended to all description 96 | # unit titles (such as .. function::). 97 | #add_module_names = True 98 | 99 | # If true, sectionauthor and moduleauthor directives will be shown in the 100 | # output. They are ignored by default. 101 | #show_authors = False 102 | 103 | # The name of the Pygments (syntax highlighting) style to use. 104 | pygments_style = 'sphinx' 105 | 106 | # A list of ignored prefixes for module index sorting. 107 | #modindex_common_prefix = [] 108 | 109 | # If true, keep warnings as "system message" paragraphs in the built documents. 110 | #keep_warnings = False 111 | 112 | # If true, `todo` and `todoList` produce output, else they produce nothing. 113 | todo_include_todos = True 114 | 115 | 116 | # -- Options for HTML output ---------------------------------------------- 117 | 118 | # The theme to use for HTML and HTML Help pages. See the documentation for 119 | # a list of builtin themes. 120 | html_theme = 'sphinxdoc' 121 | 122 | # Theme options are theme-specific and customize the look and feel of a theme 123 | # further. For a list of options available for each theme, see the 124 | # documentation. 125 | #html_theme_options = {} 126 | 127 | # Add any paths that contain custom themes here, relative to this directory. 128 | #html_theme_path = [] 129 | 130 | # The name for this set of Sphinx documents. If None, it defaults to 131 | # " v documentation". 132 | #html_title = None 133 | 134 | # A shorter title for the navigation bar. Default is the same as html_title. 135 | #html_short_title = None 136 | 137 | # The name of an image file (relative to this directory) to place at the top 138 | # of the sidebar. 139 | #html_logo = None 140 | 141 | # The name of an image file (within the static path) to use as favicon of the 142 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 143 | # pixels large. 144 | #html_favicon = None 145 | 146 | # Add any paths that contain custom static files (such as style sheets) here, 147 | # relative to this directory. They are copied after the builtin static files, 148 | # so a file named "default.css" will overwrite the builtin "default.css". 149 | html_static_path = ['_static'] 150 | 151 | # Add any extra paths that contain custom files (such as robots.txt or 152 | # .htaccess) here, relative to this directory. These files are copied 153 | # directly to the root of the documentation. 154 | #html_extra_path = [] 155 | 156 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 157 | # using the given strftime format. 158 | #html_last_updated_fmt = '%b %d, %Y' 159 | 160 | # If true, SmartyPants will be used to convert quotes and dashes to 161 | # typographically correct entities. 162 | #html_use_smartypants = True 163 | 164 | # Custom sidebar templates, maps document names to template names. 165 | #html_sidebars = {} 166 | 167 | # Additional templates that should be rendered to pages, maps page names to 168 | # template names. 169 | #html_additional_pages = {} 170 | 171 | # If false, no module index is generated. 172 | #html_domain_indices = True 173 | 174 | # If false, no index is generated. 175 | #html_use_index = True 176 | 177 | # If true, the index is split into individual pages for each letter. 178 | #html_split_index = False 179 | 180 | # If true, links to the reST sources are added to the pages. 181 | #html_show_sourcelink = True 182 | 183 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 184 | #html_show_sphinx = True 185 | 186 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 187 | #html_show_copyright = True 188 | 189 | # If true, an OpenSearch description file will be output, and all pages will 190 | # contain a tag referring to it. The value of this option must be the 191 | # base URL from which the finished HTML is served. 192 | #html_use_opensearch = '' 193 | 194 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 195 | #html_file_suffix = None 196 | 197 | # Language to be used for generating the HTML full-text search index. 198 | # Sphinx supports the following languages: 199 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 200 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 201 | #html_search_language = 'en' 202 | 203 | # A dictionary with options for the search language support, empty by default. 204 | # Now only 'ja' uses this config value 205 | #html_search_options = {'type': 'default'} 206 | 207 | # The name of a javascript file (relative to the configuration directory) that 208 | # implements a search results scorer. If empty, the default will be used. 209 | #html_search_scorer = 'scorer.js' 210 | 211 | # Output file base name for HTML help builder. 212 | htmlhelp_basename = 'pictureComparedoc' 213 | 214 | # -- Options for LaTeX output --------------------------------------------- 215 | 216 | latex_elements = { 217 | # The paper size ('letterpaper' or 'a4paper'). 218 | #'papersize': 'letterpaper', 219 | 220 | # The font size ('10pt', '11pt' or '12pt'). 221 | #'pointsize': '10pt', 222 | 223 | # Additional stuff for the LaTeX preamble. 224 | #'preamble': '', 225 | 226 | # Latex figure (float) alignment 227 | #'figure_align': 'htbp', 228 | } 229 | 230 | # Grouping the document tree into LaTeX files. List of tuples 231 | # (source start file, target name, title, 232 | # author, documentclass [howto, manual, or own class]). 233 | latex_documents = [ 234 | (master_doc, 'pictureCompare.tex', u'pictureCompare Documentation', 235 | u'Neo', 'manual'), 236 | ] 237 | 238 | # The name of an image file (relative to this directory) to place at the top of 239 | # the title page. 240 | #latex_logo = None 241 | 242 | # For "manual" documents, if this is true, then toplevel headings are parts, 243 | # not chapters. 244 | #latex_use_parts = False 245 | 246 | # If true, show page references after internal links. 247 | #latex_show_pagerefs = False 248 | 249 | # If true, show URL addresses after external links. 250 | #latex_show_urls = False 251 | 252 | # Documents to append as an appendix to all manuals. 253 | #latex_appendices = [] 254 | 255 | # If false, no module index is generated. 256 | #latex_domain_indices = True 257 | 258 | 259 | # -- Options for manual page output --------------------------------------- 260 | 261 | # One entry per manual page. List of tuples 262 | # (source start file, name, description, authors, manual section). 263 | man_pages = [ 264 | (master_doc, 'picturecompare', u'pictureCompare Documentation', 265 | [author], 1) 266 | ] 267 | 268 | # If true, show URL addresses after external links. 269 | #man_show_urls = False 270 | 271 | 272 | # -- Options for Texinfo output ------------------------------------------- 273 | 274 | # Grouping the document tree into Texinfo files. List of tuples 275 | # (source start file, target name, title, author, 276 | # dir menu entry, description, category) 277 | texinfo_documents = [ 278 | (master_doc, 'pictureCompare', u'pictureCompare Documentation', 279 | author, 'pictureCompare', 'One line description of project.', 280 | 'Miscellaneous'), 281 | ] 282 | 283 | # Documents to append as an appendix to all manuals. 284 | #texinfo_appendices = [] 285 | 286 | # If false, no module index is generated. 287 | #texinfo_domain_indices = True 288 | 289 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 290 | #texinfo_show_urls = 'footnote' 291 | 292 | # If true, do not generate a @detailmenu in the "Top" node's menu. 293 | #texinfo_no_detailmenu = False 294 | 295 | 296 | # -- Options for Epub output ---------------------------------------------- 297 | 298 | # Bibliographic Dublin Core info. 299 | epub_title = project 300 | epub_author = author 301 | epub_publisher = author 302 | epub_copyright = copyright 303 | 304 | # The basename for the epub file. It defaults to the project name. 305 | #epub_basename = project 306 | 307 | # The HTML theme for the epub output. Since the default themes are not optimized 308 | # for small screen space, using the same theme for HTML and epub output is 309 | # usually not wise. This defaults to 'epub', a theme designed to save visual 310 | # space. 311 | #epub_theme = 'epub' 312 | 313 | # The language of the text. It defaults to the language option 314 | # or 'en' if the language is not set. 315 | #epub_language = '' 316 | 317 | # The scheme of the identifier. Typical schemes are ISBN or URL. 318 | #epub_scheme = '' 319 | 320 | # The unique identifier of the text. This can be a ISBN number 321 | # or the project homepage. 322 | #epub_identifier = '' 323 | 324 | # A unique identification for the text. 325 | #epub_uid = '' 326 | 327 | # A tuple containing the cover image and cover page html template filenames. 328 | #epub_cover = () 329 | 330 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 331 | #epub_guide = () 332 | 333 | # HTML files that should be inserted before the pages created by sphinx. 334 | # The format is a list of tuples containing the path and title. 335 | #epub_pre_files = [] 336 | 337 | # HTML files shat should be inserted after the pages created by sphinx. 338 | # The format is a list of tuples containing the path and title. 339 | #epub_post_files = [] 340 | 341 | # A list of files that should not be packed into the epub file. 342 | epub_exclude_files = ['search.html'] 343 | 344 | # The depth of the table of contents in toc.ncx. 345 | #epub_tocdepth = 3 346 | 347 | # Allow duplicate toc entries. 348 | #epub_tocdup = True 349 | 350 | # Choose between 'default' and 'includehidden'. 351 | #epub_tocscope = 'default' 352 | 353 | # Fix unsupported image types using the Pillow. 354 | #epub_fix_images = False 355 | 356 | # Scale large images. 357 | #epub_max_image_width = 0 358 | 359 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 360 | #epub_show_urls = 'inline' 361 | 362 | # If false, no index is generated. 363 | #epub_use_index = True 364 | 365 | 366 | # Example configuration for intersphinx: refer to the Python standard library. 367 | intersphinx_mapping = {'https://docs.python.org/': None} 368 | -------------------------------------------------------------------------------- /docs/source/develop/main.rst: -------------------------------------------------------------------------------- 1 | ###################### 2 | 开发文档 3 | ###################### 4 | 5 | 不积跬步,无以至千里;不积小流,无以成江海。 6 | ... -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. _content 2 | 3 | pictureCompare 使用手册! 4 | ========================================== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | start/main 10 | develop/main 11 | 12 | 快捷查询 13 | ================== 14 | 15 | * :ref:`genindex` 16 | * :ref:`search` 17 | -------------------------------------------------------------------------------- /docs/source/start/main.rst: -------------------------------------------------------------------------------- 1 | ###################### 2 | 开始使用 3 | ###################### 4 | 5 | 6 | 7 | 安装python 8 | --------------- 9 | 10 | 是的需要安装python2.7*版本 ,并且依据readme.md 进行安装 11 | 12 | 开始使用 13 | --------------- 14 | 15 | pictureCompare 是更具图片色彩直方图,以及汉明距离等算法加工而成的一个图片对比工具。 16 | 17 | .. warning:: 18 | 19 | 内容有待完善,欢迎加入开发 20 | 21 | 安装好了后可在浏览器 打开:: 22 | 23 | http://localhost:5555 24 | 25 | 26 | 查看演示demo。 27 | 28 | 29 | ==== 30 | 开始搜索吧!! 31 | ==== 32 | 33 | url:: 34 | 35 | http://:/_ 36 | 37 | http method:: 38 | 39 | post 40 | 41 | .. code:: 42 | 43 | { 44 | "query" : { 45 | "url" : "http://i.gzdmc.net/sku/ebe88c67a2fd931878ded89cb1b726ec.jpg@1e_50w_50h_1c_0i_1o_90Q_1x.png" 46 | } 47 | } 48 | ==== 49 | 创建索引 50 | ==== 51 | 52 | ==== 53 | 使用api 54 | ==== 55 | 56 | url:: 57 | 58 | http://:/_index?type=json 59 | 60 | 61 | .. code:: 62 | 63 | { 64 | "query" : { 65 | "url" : "https://www.baidu.com/img/bd_logo1.png", 66 | "name" : "store one", 67 | "id" : "1" 68 | } 69 | } 70 | 71 | compare image url:: 72 | 73 | http://:/_pc?type=json 74 | 75 | .. code:: 76 | 77 | { 78 | "query" : { 79 | "url" : "https://www.baidu.com/img/bd_logo1.png" 80 | } 81 | } 82 | 83 | 84 | upload url:: 85 | 86 | http://:/_upload?type=data 87 | 88 | .. code:: 89 | 90 | { 91 | name : file_img 92 | body :data 93 | } 94 | 95 | 96 | 97 | mix upload and get json url:: 98 | 99 | http://:/_mix?type=data 100 | 101 | .. code:: 102 | 103 | { 104 | name : file_img|file 105 | body :data 106 | } 107 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tornado==4.4.1 2 | lxml==3.4.4 3 | numpy==1.9.2 4 | pillow==2.9.0 5 | tinydb==2.3.2 6 | pyyaml==3.12 7 | redis==2.10.5 8 | opencv-python==3.3.0.9 9 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import importlib 4 | 5 | if __name__ == "__main__": 6 | from server import config_yaml 7 | config_yaml() 8 | 9 | for testcase in [ 10 | 'tests.test_base' 11 | ]: 12 | __instance = importlib.import_module(testcase) 13 | __instance.main() 14 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import os 3 | import yaml 4 | import redis 5 | import tornado 6 | import tornado.web 7 | import tornado.template 8 | import tornado.web 9 | import tornado.ioloop 10 | import tornado.httpserver 11 | from tornado.options import define, options 12 | from tornado.ioloop import IOLoop 13 | 14 | from src import routes 15 | 16 | def config_yaml(): 17 | ''' 18 | config the App value 19 | :return: 20 | ''' 21 | yaml_config = yaml.load(open("./config.yaml")) 22 | # 全局可用变量 23 | # config = yaml_config 24 | if not 'redis' in yaml_config.keys(): 25 | raise Exception('config error not redis config') 26 | if not 'port' in yaml_config.keys(): 27 | raise Exception('config error not port key') 28 | if not 'binding_host' in yaml_config.keys(): 29 | raise Exception('config error not binding_host key') 30 | if not 'result_size' in yaml_config.keys(): 31 | raise Exception('config error not result_size key') 32 | # 做检查 33 | return yaml_config 34 | 35 | config = config_yaml() 36 | 37 | class Application(tornado.web.Application): 38 | def __init__(self): 39 | ''' the setting ''' 40 | settings = dict( 41 | template_path = os.path.join(os.path.dirname(__file__), "src/template"), 42 | static_path = os.path.join(os.path.dirname(__file__), "src/static"), 43 | autoreload=False, 44 | debug=False, 45 | # autoreload = True, 46 | # debug = True 47 | ) 48 | 49 | self.config = config 50 | route = routes.getRoutes(config) 51 | 52 | _host = self.config['redis']['host'] 53 | _port = self.config['redis']['port'] 54 | _db = self.config['redis']['db'] 55 | # 需要认真阅读这里的文章 56 | # https://mirrors.segmentfault.com/itt2zh/ch4.html 57 | self.r = redis.Redis( 58 | host = _host, 59 | port = _port, 60 | db = _db 61 | ) 62 | 63 | # 初始化 redis 64 | # 服务器初始化 65 | tornado.web.Application.__init__(self, handlers=route, **settings) 66 | 67 | # start run 68 | def main(): 69 | print 'Running...' 70 | # 服务启动的进程 71 | num_processes = 1 72 | app_port = config['port'] 73 | address = config['binding_host'] 74 | tornado.options.parse_command_line() 75 | http_server = tornado.httpserver.HTTPServer(Application()) 76 | 77 | # listen 可以显式创建创建 http 78 | # http_server.listen(app_port) 79 | http_server.bind(app_port) 80 | http_server.start(num_processes) 81 | # IOLoop.current().start() 82 | IOLoop.instance().start() 83 | 84 | # main run 85 | if __name__ == "__main__": 86 | main() 87 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/src/__init__.py -------------------------------------------------------------------------------- /src/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/src/core/__init__.py -------------------------------------------------------------------------------- /src/core/app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json, time, os, uuid 3 | import hashlib 4 | 5 | from tornado import gen 6 | import tornado 7 | import tornado.ioloop 8 | import tornado.web 9 | import tornado.gen 10 | import tornado.template 11 | 12 | from src.lib.data import Data 13 | from src.service.manage import Manage 14 | from src.service.match import Match # 对比管理程序 15 | 16 | class App(tornado.web.RequestHandler): 17 | SUPPORTED_METHODS = ("CONNECT", "GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", "OPTIONS") 18 | # def __init__(self, application, request, **kwargs): 19 | # super(tornado.web.RequestHandler, self).__init__(*request,**kwargs) 20 | def __init__(self, *request, **kwargs): 21 | super(App, self).__init__(request[0], request[1]) 22 | 23 | self.set_header('Access-Control-Allow-Origin', '*') 24 | self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') 25 | 26 | self.data = Data() 27 | self.config = self.application.config 28 | self.manage = Manage( 29 | self.application.r 30 | ) 31 | 32 | # 定义 匹配接口 33 | self.match = Match( 34 | self.manage, 35 | self.config['result_size'] 36 | ) 37 | # 数据结果 38 | def result(self, resultDict = []): 39 | # print compareDict 40 | self.write(self.data 41 | .set('status', 'OK') 42 | .set('data', resultDict) 43 | .get()) 44 | 45 | def add_json(self): 46 | self.set_header('Content-Type', 'application/json') 47 | 48 | def write_error(self, status_code, **kwargs): 49 | ''' 50 | : 处理失败问题 51 | ''' 52 | self.set_status(200) 53 | self.set_header('Content-Type', 'application/json') 54 | self.write( 55 | self.data 56 | .set('status', 'fail') 57 | .set('message', 'Not any result be found') 58 | .set('data', []) 59 | .get() 60 | ) 61 | -------------------------------------------------------------------------------- /src/detector.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import io 4 | 5 | # define a base detector Image paser for module 6 | class Detector: 7 | _weight = 1 8 | # 储存 原 对比数据, 避免又一次计算 9 | _base_image = None 10 | _base_image_for_opencv = None 11 | # 注册名称 12 | def get_name(self): 13 | return self.__class__.__name__ 14 | 15 | def __init__(self): 16 | pass 17 | 18 | def match(self, source, target): 19 | source_path = source.get_path() 20 | target_path = target.get_path() 21 | 22 | def do_screening(self): 23 | raise Exception('You must rewrite the do screening function!!') 24 | 25 | def get_score(self): 26 | return self.do_screening() 27 | 28 | # def reg(self): 29 | # # 注册该方法到 feature 30 | # raise Exception('You must rewrite the do screening function!!') 31 | 32 | def calculate(self, origin = None, local = None): 33 | # 开始计算 34 | raise Exception('You must rewrite the calculate function!!') 35 | 36 | def is_io(self, io_image = None): 37 | if io is None: 38 | raise Exception('io is error') 39 | if not isinstance(io_image, io.BytesIO): 40 | return False 41 | return True 42 | -------------------------------------------------------------------------------- /src/detectors/Base.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import math 3 | import operator 4 | from PIL import Image as im 5 | from src.detector import Detector 6 | 7 | # histogram 算法 8 | class Base(Detector): 9 | 10 | def calculate(self, origin = None, local = None): 11 | return self._base(origin, local) 12 | 13 | # ---------------------------------------------------------- 14 | # base 15 | # 算法 -> 直方图比较 16 | # ---------------------------------------------------------- 17 | def _base(self, origin = None, local = None): 18 | # origin 需要 静态化 19 | if self._base_image is None: 20 | print 'histogram calculate' 21 | image1 = im.open(origin) 22 | self._base_image = image1.convert('RGB').histogram() 23 | 24 | # if not image1.size is image2.size: 25 | # image2 = image2.resize(image1.size) 26 | # pass 27 | 28 | image2 = im.open(local) 29 | h2 = image2.convert('RGB').histogram() 30 | 31 | rms = math.sqrt( 32 | reduce( 33 | operator.add, 34 | list( 35 | map( 36 | lambda a, b: (a - b) ** 2, self._base_image, h2 37 | ) 38 | ) 39 | ) 40 | / 41 | len(self._base_image) 42 | ) 43 | print rms 44 | # 如果这个 histigram 波浪太大就过滤掉 45 | # wave = reduce(lambda o,n: abs(0-n),image2.convert('RGB').histogram()) 46 | # return wave 47 | 48 | return rms 49 | -------------------------------------------------------------------------------- /src/detectors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/src/detectors/__init__.py -------------------------------------------------------------------------------- /src/detectors/color.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from PIL import Image as im 4 | from src.detector import Detector 5 | 6 | class Color(Detector): 7 | 8 | def calculate(self, origin = None, local = None): 9 | # 注册 执行 perceptual hash 10 | return self._color(origin, local) 11 | 12 | # ---------------------------------------------------------- 13 | # color compare 14 | # 15 | # ---------------------------------------------------------- 16 | def _color(self, origin=None, local=None): 17 | ''' 18 | 计算两个三维向量距离 19 | (R1-R2)^2 + (G1-G2)^2 + (B1-B2)^2 的值的平方根,即颜色空间的距离 20 | 距离越大,差距就越大。 21 | :return: 22 | ''' 23 | def getRgb(io): 24 | r, g, b = im.open(io).convert('RGB').resize((1, 1)).getcolors()[0][1] 25 | return [r, g, b] 26 | RGB_A = None 27 | if self._base_image is None: 28 | RGB_A = getRgb(origin) 29 | 30 | RGB_B = getRgb(local) 31 | 32 | # if len(RGB_A) == 3 and len(RGB_B) == 3: 33 | score = (RGB_A[0] - RGB_B[0]) ** 2 + (RGB_A[1] - RGB_B[1]) ** 2 + (RGB_A[2] - RGB_B[2]) ** 2 34 | return abs(score) 35 | 36 | -------------------------------------------------------------------------------- /src/detectors/correlate.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from PIL import Image as im 4 | from src.detector import Detector 5 | 6 | class Phash(Detector): 7 | 8 | def calculate(self, origin = None, local = None): 9 | # 注册 执行 perceptual hash 10 | return self._mse(origin, local) 11 | 12 | # ---------------------------------------------------------- 13 | # correlate2d 14 | # very slow 15 | # return int 16 | # return the MSE, the lower the error, the more "similar" 17 | # NOTE: the two images must have the same dimension 18 | # ---------------------------------------------------------- 19 | def correlate2d(self): 20 | """ 21 | : So fucking slow 22 | : return: float 23 | """ 24 | import scipy as sp 25 | from scipy.misc import imread 26 | from scipy.signal.signaltools import correlate2d 27 | 28 | def get(path): 29 | data = imread(path) 30 | data = sp.inner(data, [299, 587, 114]) / 1000.0 31 | return (data - data.mean()) / data.std() 32 | 33 | value = correlate2d(get(self.image_a_path), 34 | get(self.image_b_path)).max() 35 | return value 36 | -------------------------------------------------------------------------------- /src/detectors/mse.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from PIL import Image as im 4 | from src.detector import Detector 5 | import numpy as np 6 | import cv2 7 | # from StringIO import StringIO 8 | 9 | class Mse(Detector): 10 | 11 | def calculate(self, origin = None, local = None): 12 | # 注册 执行 perceptual hash 13 | return self._mse(origin, local) 14 | 15 | # ---------------------------------------------------------- 16 | # mse 均方误差 17 | # MSE可以评价数据的变化程度, 18 | # MSE的值越小, 19 | # 说明预测模型描述实验数据具有更好的精确度。与此相对应的, 20 | # 还有均方根误差RMSE、平均绝对百分误差等等。 21 | # ---------------------------------------------------------- 22 | def _mse(self, origin = None, local = None): 23 | """ 24 | :return: float 25 | """ 26 | # 使用 io byte 读取数据 27 | # import numpy as np 28 | # a1=io.BytesIO(rr.get('base_image_name')) 29 | # nparr = np.fromstring(a1.read(), np.uint8) 30 | # img_np = cv2.imdecode(nparr, cv2.CV_LOAD_IMAGE_COLOR) 31 | # cv2.imshow('image',img_np) 32 | # cv2.namedWindow('image', cv2.WINDOW_NORMAL) 33 | # cv2.imshow('image',img) 34 | # cv2.waitKey(0) 35 | # cv2.destroyAllWindows() 36 | 37 | def io_to_cv2data(io = None): 38 | nparr = np.fromstring(io.read(), np.uint8) 39 | return cv2.imdecode(nparr, cv2.CV_LOAD_IMAGE_COLOR) 40 | 41 | if self._base_image_for_opencv is None: 42 | self._base_image_for_opencv = io_to_cv2data(origin) 43 | 44 | # use opencv color bgr to gray 45 | imageA = cv2.cvtColor(self._base_image_for_opencv, cv2.COLOR_BGR2GRAY) 46 | imageB = cv2.cvtColor(io_to_cv2data(local), cv2.COLOR_BGR2GRAY) 47 | 48 | err = np.sum((imageA.astype("float") - imageB.astype("float")) ** 2) 49 | err /= float(imageA.shape[0] * imageA.shape[1]) 50 | 51 | return err 52 | 53 | -------------------------------------------------------------------------------- /src/detectors/phash.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from PIL import Image as im 4 | from src.detector import Detector 5 | 6 | class Phash(Detector): 7 | 8 | def calculate(self, origin = None, local = None): 9 | # 注册 执行 perceptual hash 10 | return self._phash(origin, local) 11 | 12 | # ---------------------------------------------------------- 13 | # perceptual Hash 14 | # very quick 8 x 8 15 | # 感知 hash 算法 , 通过指纹匹配 16 | # ---------------------------------------------------------- 17 | def _phash(self, origin = None, local = None): 18 | def avhash(io, im): 19 | # print im 20 | # print im 21 | # if not isinstance(im, Image.Image): 22 | the_image = im.open(io) 23 | the_image = the_image.resize((8, 8), im.ANTIALIAS).convert('L') 24 | avg = reduce(lambda x, y: x + y, the_image.getdata()) / 64. 25 | return reduce( 26 | lambda x, (y, z): x | (z << y), 27 | enumerate(map(lambda i: 0 if i < avg else 1, the_image.getdata())), 28 | 0 29 | ) 30 | 31 | # hamming 距离 32 | def hamming(h1, h2): 33 | h, d = 0, h1 ^ h2 34 | while d: 35 | h += 1 36 | d &= d - 1 37 | return h 38 | 39 | # a = avhash(self.byte_base, im) 40 | # b = avhash(self.byte_storage, im) 41 | 42 | # a 需要 静态化 43 | if self._base_image is None: 44 | print 'phash calculate again' 45 | self._base_image = avhash(origin, im) 46 | 47 | b = avhash(local, im) 48 | value = hamming(self._base_image, b) 49 | # self.value_of_perceptualHash = value 50 | 51 | return value 52 | -------------------------------------------------------------------------------- /src/handler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/src/handler/__init__.py -------------------------------------------------------------------------------- /src/handler/bindex.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import hashlib 3 | import json, time, os, uuid 4 | import tornado 5 | import tornado.ioloop 6 | import tornado.web 7 | import tornado.gen 8 | import tornado.template 9 | from src.lib.data import Data 10 | 11 | # from tornado import queues 12 | from tornado import gen 13 | 14 | from tinydb import TinyDB, where 15 | from src.service.manage import Manage 16 | 17 | class BuildIndexHandler(tornado.web.RequestHandler): 18 | """ 19 | RESTFUL api style 20 | """ 21 | def get(self, type=None): 22 | self.write("please use post method") 23 | 24 | @tornado.gen.coroutine 25 | def post(self, type=None): 26 | self.set_header('Content-Type', 'application/json') 27 | jsonM = Data() 28 | 29 | getJson = self.request.body 30 | jsondata = json.loads(getJson) 31 | 32 | __is_base64 = False 33 | __image = None 34 | if 'url' in jsondata['query'].keys(): 35 | __image = jsondata['query']['url'] 36 | else: 37 | __image = jsondata['query']['base64'] 38 | __is_base64 = True 39 | 40 | if __image is None: 41 | raise Exception('image data is empty!') 42 | 43 | __name = jsondata['query']['name'] 44 | __id = jsondata['query']['id'] 45 | __data = jsondata['query']['data'] 46 | # 需要参与搜索的字段 47 | __search = jsondata['query']['search'] 48 | 49 | __data['id'] = __id 50 | # 直接使用 application 的 redis 初始化 51 | Manage( 52 | self.application.r 53 | ).index_image( 54 | __id, 55 | __search, 56 | __data, 57 | __image, 58 | __name, 59 | __is_base64 60 | ) 61 | return self.write(jsonM.setStatus('status', 'OK') 62 | .set('msg', str('index success!')) 63 | .get()) 64 | 65 | from src.core.app import App 66 | 67 | class AddHandler(App): 68 | def get(self): 69 | return self.write('[]') 70 | 71 | class CleaerIndexHandler(tornado.web.RequestHandler): 72 | def delete(self, type=None): 73 | try: 74 | # os.remove(os.environ[config.STORAGE_INDEX_DB]) 75 | Manage( 76 | self.application.r 77 | ).clear_db() 78 | except: 79 | pass 80 | self.set_header('Content-Type', 'application/json') 81 | jsonM = Data() 82 | self.write(jsonM.setStatus('status', 'OK') 83 | .set('msg', str('delete index Success!')) 84 | .get()) 85 | def get(self, type=None): 86 | self.write("error method") 87 | -------------------------------------------------------------------------------- /src/handler/compare.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import json 3 | from src.core.app import App 4 | 5 | class pcHandler(App): 6 | def post(self, type): 7 | self.set_header('Content-Type', 'application/json') 8 | # way = self.get_argument("type") 9 | 10 | # if way != 'json': 11 | # raise Exception('method incorrect!') 12 | 13 | getJson = self.request.body 14 | jsondata = json.loads(getJson) 15 | 16 | # 储存对比图片到 redis 17 | if 'base64' in jsondata['query'].keys(): 18 | self.manage.store_base_image_by_base64(jsondata['query']['base64']) 19 | else: 20 | self.manage.store_base_image(jsondata['query']['url']) 21 | 22 | try: 23 | terms = jsondata['terms'] 24 | except: 25 | terms = None 26 | 27 | # 返回数据限定 28 | try: 29 | page_size = jsondata['size'] 30 | except: 31 | page_size = None 32 | 33 | # 开始比对 34 | resultDict = self.match.get_match_result(terms, page_size) 35 | 36 | self.result(resultDict) 37 | -------------------------------------------------------------------------------- /src/handler/demo.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import os 3 | import glob 4 | import StringIO 5 | import tornado 6 | import tornado.ioloop 7 | import tornado.web 8 | import tornado.template 9 | from PIL import Image as im 10 | 11 | from src.core.app import App 12 | from src.lib.image import Image 13 | from src.service.feature import Feature as Compare 14 | from src.service.manage import Manage as Manage 15 | from src.service.match import Match 16 | 17 | 18 | 19 | class TestHandler(App): 20 | def get(self, *args, **kwargs): 21 | self.write( 22 | tornado.template.Loader(os.environ[config.TEMPLATE]).load("search_test_upload.html").generate( 23 | name='search by index', 24 | action='search_test' 25 | ) 26 | ) 27 | 28 | def post(self, *args, **kwargs): 29 | ''' 30 | search color test demo 31 | ''' 32 | os.chdir(os.environ[config.PROJECT_DIR] + "tests/tmp/") 33 | 34 | imagetool = im 35 | compare = Compare() 36 | imgfiles = self.request.files['file_img'] 37 | if len(imgfiles) > 1: 38 | return self.write("error") 39 | imgfile = imgfiles[0] 40 | filename = imgfile['filename'].strip() 41 | import uuid 42 | filename = str(uuid.uuid4()) + '.' + filename.split('.')[-1] 43 | tmp = os.environ[config.PROJECT_DIR] + "tests/tmp/" 44 | 45 | tmp_image = tmp + filename 46 | print tmp_image 47 | im.open(StringIO.StringIO(imgfile['body'])).save(tmp_image) 48 | 49 | # path = os.environ[config.PROJECT_DIR] + "tests/img/" 50 | self.m.store_base_image_file(tmp_image) 51 | 52 | 53 | # 开始比对 54 | resultDict = Match( 55 | self.m 56 | ).get_match_result() 57 | 58 | print resultDict 59 | self.write( 60 | tornado.template.Loader(os.environ[config.TEMPLATE]).load("search_test.html").generate( 61 | origin_img = '/tests/tmp/' + filename, 62 | results = resultDict 63 | ) 64 | ) 65 | -------------------------------------------------------------------------------- /src/handler/home.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import os 3 | import tornado 4 | import tornado.ioloop 5 | import tornado.web 6 | import tornado.template 7 | from src.core.app import App 8 | from src.lib.data import Data 9 | 10 | class HomeHandler(App): 11 | # @tornado.web.asynchronous 12 | def get(self, param): 13 | self.set_header('Content-Type', 'application/json') 14 | the_list = self.manage.search([]) 15 | result = self.data \ 16 | .set('status', 'ok') \ 17 | .set('count_data', str(len(the_list))) \ 18 | .set('config', self.config) \ 19 | .get() 20 | self.write( 21 | result 22 | ) 23 | -------------------------------------------------------------------------------- /src/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/src/lib/__init__.py -------------------------------------------------------------------------------- /src/lib/data.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import json 3 | from StringIO import StringIO 4 | 5 | class Data: 6 | ''' 7 | this is pc json standard 8 | The json must be have status and data 9 | status is a str 10 | data was dict 11 | ''' 12 | def __init__(self, data = []): 13 | self.origin = data 14 | self.data = {} 15 | 16 | def __str__(self): 17 | ''' 18 | :return: 19 | ''' 20 | return "json_module" 21 | 22 | def setData(self, key, param): 23 | ''' 24 | :param key: 25 | :param param: 26 | :return: 27 | ''' 28 | if type(param) in (str, list, dict): 29 | if key is 'data': 30 | self.data[key] = param 31 | return self 32 | 33 | def setStatus(self, key, param): 34 | ''' 35 | :param key: 36 | :param param: 37 | :return: 38 | ''' 39 | if type(param) in (str, list, dict): 40 | if key is 'status': 41 | self.data[key] = param 42 | return self 43 | 44 | def set(self, key, param): 45 | ''' 46 | :param key: 47 | :param param: 48 | :return: 49 | ''' 50 | if type(param) in (str, list, dict): 51 | self.data[key] = param 52 | return self 53 | 54 | def add(self, key, param): 55 | ''' 56 | :param key: 57 | :param param: 58 | :return: 59 | ''' 60 | self.data[key].append(param) 61 | 62 | def get(self): 63 | ''' 64 | :return: 65 | ''' 66 | return json.dumps(self.data) 67 | 68 | def loads(self, raw_data = ''): 69 | return json.loads(raw_data) 70 | 71 | def to_string(self): 72 | return json.dumps(self.origin) 73 | -------------------------------------------------------------------------------- /src/lib/image.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | from PIL import Image as im 3 | 4 | class Image: 5 | ''' 6 | tranlate value for color 7 | ''' 8 | 9 | def __init__(self): 10 | pass 11 | 12 | # ---------------------------------------------------------- 13 | # get picture color rgba 14 | # @:return 15 | # ---------------------------------------------------------- 16 | def get_rgba(self, io): 17 | ''' 18 | :param path: 19 | :return: 20 | ''' 21 | r, g, b, a = im.open(io).convert('RGBA').resize((1, 1)).getcolors()[0][1] 22 | return (r, g, b, a) 23 | 24 | 25 | # ---------------------------------------------------------- 26 | # get picture color rgb 27 | # @:return 28 | # ---------------------------------------------------------- 29 | def get_rgb(self, io): 30 | ''' 31 | :param path: 32 | :return: 33 | ''' 34 | r, g, b = im.open(io).convert('RGB').resize((1, 1)).getcolors()[0][1] 35 | return (r, g, b) 36 | 37 | def get_hsi(self, io): 38 | ''' 39 | Unfortunately I think that this transformation can not be done 40 | with Image.convert: you can only do transformations between equivalent 41 | color spaces (i.e. reach the destination colorspace by doing a 42 | multiplication with a matrix with the source colorspace). 43 | 44 | The HSI colorspace, instead, is not an equivalent colorspace of RGB. 45 | To get the HSI equivalent of a RGB pixel one must do these 46 | transformations: 47 | 48 | :return: 49 | ''' 50 | import math 51 | 52 | R, G, B = im.open(io).convert('RGB').resize((1, 1)).getcolors()[0][1] 53 | 54 | I = 1/3 * (R+G+B) 55 | 56 | S = 1 - (3/(R+G+B))*(min(R,G,B)) 57 | 58 | H = math.cos^-1( (((R-G)+(R-B))/2)/ (math.sqrt((R-G)^2 + (R-B)*(G-B) ))) 59 | 60 | return (H,S,I) 61 | 62 | rgb_list = [ 63 | {"almon": (250, 128, 114)}, 64 | {"darksalmon": (233, 150, 122)}, 65 | {"lightcoral": (240, 128, 128)}, 66 | {"indianred": (205, 92, 92)}, 67 | {"crimson": (220, 20, 60)}, 68 | {"firebrick": (178, 34, 34)}, 69 | {"red": (255, 0, 0)}, 70 | {"darkred": (139, 0, 0)}, 71 | {"coral": (255, 127, 80)}, 72 | {"tomato": (255, 99, 71)}, 73 | {"orangered": (255, 69, 0)}, 74 | {"gold": (255, 215, 0)}, 75 | {"orange": (255, 165, 0)}, 76 | {"darkorange": (255, 140, 0)}, 77 | {"lightyellow": (255, 255, 224)}, 78 | {"lemonchiffon": (255, 250, 205)}, 79 | {"lightgoldenrodyellow": (250, 250, 210)}, 80 | {"papayawhip": (255, 239, 213)}, 81 | {"moccasin": (255, 228, 181)}, 82 | {"peachpuff": (255, 218, 185)}, 83 | {"palegoldenrod": (238, 232, 170)}, 84 | {"khaki": (240, 230, 140)}, 85 | {"darkkhaki": (189, 183, 107)}, 86 | {"yellow": (255, 255, 0)}, 87 | {"lawngreen": (124, 252, 0)}, 88 | {"chartreuse": (127, 255, 0)}, 89 | {"limegreen": (50, 205, 50)}, 90 | {"lime": (0, 255, 0)}, 91 | {"forestgreen": (34, 139, 34)}, 92 | {"green": (0, 128, 0)}, 93 | {"darkgreen": (0, 100, 0)}, 94 | {"greenyellow": (173, 255, 47)}, 95 | {"yellowgreen": (154, 205, 50)}, 96 | {"springgreen": (0, 255, 127)}, 97 | {"mediumspringgreen": (0, 250, 154)}, 98 | {"lightgreen": (144, 238, 144)}, 99 | {"palegreen": (152, 251, 152)}, 100 | {"darkseagreen": (143, 188, 143)}, 101 | {"mediumseagreen": (60, 179, 113)}, 102 | {"seagreen": (46, 139, 87)}, 103 | {"olive": (128, 128, 0)}, 104 | {"darkolivegreen": (85, 107, 47)}, 105 | {"olivedrab": (107, 142, 35)}, 106 | {"lightcyan": (224, 255, 255)}, 107 | {"cyan": (0, 255, 255)}, 108 | {"aqua": (0, 255, 255)}, 109 | {"aquamarine": (127, 255, 212)}, 110 | {"mediumaquamarine": (102, 205, 170)}, 111 | {"paleturquoise": (175, 238, 238)}, 112 | {"turquoise": (64, 224, 208)}, 113 | {"mediumturquoise": (72, 209, 204)}, 114 | {"darkturquoise": (0, 206, 209)}, 115 | {"lightseagreen": (32, 178, 170)}, 116 | {"cadetblue": (95, 158, 160)}, 117 | {"darkcyan": (0, 139, 139)}, 118 | {"teal": (0, 128, 128)}, 119 | {"powderblue": (176, 224, 230)}, 120 | {"lightblue": (173, 216, 230)}, 121 | {"lightskyblue": (135, 206, 250)}, 122 | {"skyblue": (135, 206, 235)}, 123 | {"deepskyblue": (0, 191, 255)}, 124 | {"lightsteelblue": (176, 196, 222)}, 125 | {"dodgerblue": (30, 144, 255)}, 126 | {"cornflowerblue": (100, 149, 237)}, 127 | {"steelblue": (70, 130, 180)}, 128 | {"royalblue": (65, 105, 225)}, 129 | {"blue": (0, 0, 255)}, 130 | {"mediumblue": (0, 0, 205)}, 131 | {"darkblue": (0, 0, 139)}, 132 | {"navy": (0, 0, 128)}, 133 | {"midnightblue": (25, 25, 112)}, 134 | {"mediumslateblue": (123, 104, 238)}, 135 | {"slateblue": (106, 90, 205)}, 136 | {"darkslateblue": (72, 61, 139)}, 137 | {"lavender": (230, 230, 250)}, 138 | {"thistle": (216, 191, 216)}, 139 | {"plum": (221, 160, 221)}, 140 | {"violet": (238, 130, 238)}, 141 | {"orchid": (218, 112, 214)}, 142 | {"fuchsia": (255, 0, 255)}, 143 | {"magenta": (255, 0, 255)}, 144 | {"mediumorchid": (186, 85, 211)}, 145 | {"mediumpurple": (147, 112, 219)}, 146 | {"blueviolet": (138, 43, 226)}, 147 | {"darkviolet": (148, 0, 211)}, 148 | {"darkorchid": (153, 50, 204)}, 149 | {"darkmagenta": (139, 0, 139)}, 150 | {"purple": (128, 0, 128)}, 151 | {"indigo": (75, 0, 130)}, 152 | {"pink": (255, 192, 203)}, 153 | {"lightpink": (255, 182, 193)}, 154 | {"hotpink": (255, 105, 180)}, 155 | {"deeppink": (255, 20, 147)}, 156 | {"palevioletred": (219, 112, 147)}, 157 | {"mediumvioletred": (199, 21, 133)}, 158 | {"white": (255, 255, 255)}, 159 | {"snow": (255, 250, 250)}, 160 | {"honeydew": (240, 255, 240)}, 161 | {"mintcream": (245, 255, 250)}, 162 | {"azure": (240, 255, 255)}, 163 | {"aliceblue": (240, 248, 255)}, 164 | {"ghostwhite": (248, 248, 255)}, 165 | {"whitesmoke": (245, 245, 245)}, 166 | {"seashell": (255, 245, 238)}, 167 | {"beige": (245, 245, 220)}, 168 | {"oldlace": (253, 245, 230)}, 169 | {"floralwhite": (255, 250, 240)}, 170 | {"ivory": (255, 255, 240)}, 171 | {"antiquewhite": (250, 235, 215)}, 172 | {"linen": (250, 240, 230)}, 173 | {"lavenderblush": (255, 240, 245)}, 174 | {"mistyrose": (255, 228, 225)}, 175 | {"gainsboro": (220, 220, 220)}, 176 | {"lightgray": (211, 211, 211)}, 177 | {"silver": (192, 192, 192)}, 178 | {"darkgray": (169, 169, 169)}, 179 | {"gray": (128, 128, 128)}, 180 | {"dimgray": (105, 105, 105)}, 181 | {"lightslategray": (119, 136, 153)}, 182 | {"slategray": (112, 128, 144)}, 183 | {"darkslategray": (47, 79, 79)}, 184 | {"black": (0, 0, 0)}, 185 | {"cornsilk": (255, 248, 220)}, 186 | {"blanchedalmond": (255, 235, 205)}, 187 | {"bisque": (255, 228, 196)}, 188 | {"navajowhite": (255, 222, 173)}, 189 | {"wheat": (245, 222, 179)}, 190 | {"burlywood": (222, 184, 135)}, 191 | {"tan": (210, 180, 140)}, 192 | {"rosybrown": (188, 143, 143)}, 193 | {"sandybrown": (244, 164, 96)}, 194 | {"goldenrod": (218, 165, 32)}, 195 | {"peru": (205, 133, 63)}, 196 | {"chocolate": (210, 105, 30)}, 197 | {"saddlebrown": (139, 69, 19)}, 198 | {"sienna": (160, 82, 45)}, 199 | {"brown": (165, 42, 42)}, 200 | {"maroon": (128, 0, 0)} 201 | ] 202 | 203 | rgb_list_chinese = { 204 | "红色": (255, 0, 0), 205 | "橙红": (255, 51, 0), 206 | "橙色": (255, 102, 0), 207 | "橙黄": (255, 153, 0), 208 | "黄色": (255, 255, 0), 209 | "黄绿": (153, 255, 0), 210 | "绿色": (0, 255, 0), 211 | "蓝绿": (0, 255, 255), 212 | "蓝色": (0, 0, 255), 213 | "蓝紫": (102, 0, 255), 214 | "紫色": (255, 0, 255), 215 | "紫红": (255, 0, 102) 216 | } 217 | -------------------------------------------------------------------------------- /src/routes.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import tornado 3 | import tornado.ioloop 4 | import tornado.web 5 | import tornado.template 6 | from src.handler import (demo,compare,bindex,home) 7 | import os 8 | 9 | def getRoutes(config): 10 | Routes = [ 11 | # --------------------------------------------- 12 | # demo url 13 | # --------------------------------------------- 14 | (r"/search_test", demo.TestHandler), 15 | 16 | # --------------------------------------------- 17 | # static url 18 | # --------------------------------------------- 19 | # (r'/img/(.*)', tornado.web.StaticFileHandler, {'path': os.environ[config.STATIC_DIR]}), 20 | # (r'/tests/(.*)', tornado.web.StaticFileHandler, {'path': os.environ[config.PROJECT_DIR]+"/tests/"}), 21 | 22 | # --------------------------------------------- 23 | # index 24 | # --------------------------------------------- 25 | (r'/_index(.*)',bindex.BuildIndexHandler), 26 | (r'/_delete(.*)', bindex.CleaerIndexHandler), 27 | 28 | # picture compare api 29 | (r'/_pc(.*)',compare.pcHandler), 30 | 31 | # --------------------------------------------- 32 | # other tool url 33 | # --------------------------------------------- 34 | # (r'/crossdomain.xml',urltool.urltoolHandler), 35 | 36 | # --------------------------------------------- 37 | # home dashboard url 38 | # --------------------------------------------- 39 | (r"/(.*)", home.HomeHandler), 40 | ] 41 | 42 | return Routes 43 | -------------------------------------------------------------------------------- /src/service/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/src/service/__init__.py -------------------------------------------------------------------------------- /src/service/feature.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from PIL import Image as im 4 | from src.detectors.phash import Phash 5 | from src.detectors.base import Base 6 | from src.detectors.color import Color 7 | from src.detectors.mse import Mse 8 | 9 | # 方向 base 的数据其实只需要提取一次啊, 囧~~ 10 | class Feature: 11 | ''' 12 | Compare Image differ I find some useful function 13 | Maybe Some function would suit for you Project 14 | ''' 15 | detector = {} 16 | 17 | # 预注册特征 18 | __default_feature = [ 19 | # 'Phash', 20 | 'Base', 21 | # 'Color', 22 | # 'Mse', 23 | ] 24 | 25 | # 使用比特码对比 26 | byte_base = None 27 | byte_storage = None 28 | 29 | # 算法特征 30 | # 将原对比图片 预先出错 , 避免 重复计算 31 | base_image_trait_for_image = None 32 | base_image_trait_for_cv = None 33 | 34 | def __init__(self): 35 | # 注册所有的特征 36 | self.__reg(Phash()) 37 | self.__reg(Base()) 38 | self.__reg(Color()) 39 | self.__reg(Mse()) 40 | 41 | def __reg(self, instance_name = None): 42 | self.detector[ 43 | instance_name.get_name() 44 | ] = instance_name 45 | 46 | def process(self, detector_list = []): 47 | ''' 48 | : 每个进程都会使用这个 Process, 出现了一些问题?? 49 | ''' 50 | if (detector_list is None) or (not len(detector_list) > 0): 51 | detector_list = self.__default_feature 52 | 53 | result = {} 54 | for single_feature in self.detector: 55 | if single_feature in detector_list: 56 | result[single_feature] = self.detector[single_feature].calculate( 57 | self.byte_base, 58 | self.byte_storage 59 | ) 60 | return result 61 | 62 | def set_byte_base_image(self, byte): 63 | self.byte_base = byte 64 | 65 | def set_byte_storage_image(self, byte): 66 | self.byte_storage = byte 67 | -------------------------------------------------------------------------------- /src/service/manage.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import os 3 | import io 4 | import base64 5 | import urllib2 6 | import StringIO 7 | import cStringIO 8 | from tinydb import TinyDB,where 9 | 10 | from PIL import Image as im 11 | from src.lib.data import Data 12 | 13 | 14 | class Manage: 15 | image_size = (20,20) 16 | 17 | divided = '--@@--' 18 | 19 | r = None 20 | 21 | def __init__(self, redis = None): 22 | # 如今已经不需要 redis 的初始化了 叼 23 | self.r = redis 24 | 25 | # 生成短名称 短名称 在前面 26 | def __genrate_sort_name(self, names = []): 27 | ''' 28 | :生成 redis 的 key 29 | :param keys: 30 | :return: 31 | ''' 32 | __sort_dict, __result = {}, {} 33 | # 根据穷举法, 找到最短的 key 34 | for s in names: 35 | for lenght in range(0, len(s)): 36 | __check_name = s[:(lenght + 1)] 37 | if not __check_name in __sort_dict.values(): 38 | # sort name 没有被定义 可以使用 39 | __sort_dict[s] = __check_name 40 | # 同时进入下一个关键词判断 41 | break 42 | # 首先对names 进行一次排序 保证 每次的结果都一样 43 | names = sorted(__sort_dict) 44 | # 重新排序 45 | for i in names: 46 | __result[i] = __sort_dict[i] 47 | return __result 48 | 49 | # ---------------------------------------------------------- 50 | # 生成 redis key 名称 51 | # --------------------------------------------------------- 52 | def __generate_key(self, search = [] ,source_data = {}): 53 | # 生成短名称 54 | __sort_dict = self.__genrate_sort_name(search) 55 | if not type(source_data) is dict: 56 | raise Exception('Keys incorrectness!') 57 | 58 | k_name = '' 59 | 60 | for i in __sort_dict: 61 | if i in source_data: 62 | k_name += str(__sort_dict[i]) + '=' + str(source_data[i]) 63 | # 不是最后一个 那么加上 - 连接符号 64 | # if __sort_dict.keys()[-1] is not i: 65 | k_name += '-' 66 | 67 | return k_name 68 | 69 | # ---------------------------------------------------------- 70 | # 向 redis 创建索引 71 | # --------------------------------------------------------- 72 | def index_image(self, id = 0, search = [], data = [], image = '', name = '', is_base64 = False): 73 | # TOOD 需要一个算法 , 将每次 的 search 字段数据 转化为相同的,数据 74 | # 根据 client 的条件字段创建 的 key 75 | source_data = {} 76 | for index_name in search: 77 | source_data[index_name] = data[index_name] 78 | 79 | # 每个对象必须要有一个名字 80 | data['name'] = name 81 | 82 | key_name = self.__generate_key( 83 | search, 84 | source_data 85 | ) 86 | 87 | # 在尾部加上id 88 | key_name += '#' + str(id) 89 | # 0 -> data 90 | # 1 -> image - binary 91 | if image: 92 | if is_base64: 93 | # 对于 base64 的处理办法 94 | if 'base64,' in image: 95 | image = image.split(',')[1] 96 | image_str = cStringIO.StringIO(base64.b64decode(image)) 97 | im_instance = im.open(image_str).resize(self.image_size) 98 | else: 99 | # 对于 url 文件的处理办法 100 | res = urllib2.urlopen(image) 101 | if res.code == 200: 102 | # save data into Bytes 103 | imimage = io.BytesIO(res.read()) 104 | # 压缩图片以及,格式化为 JPEG 105 | im_instance = im.open(imimage).resize(self.image_size) 106 | 107 | # 不管使用哪种参数方法 最终的结果都是保存 redis 108 | output = StringIO.StringIO() 109 | im_instance \ 110 | .convert('RGB') \ 111 | .save(output, 'JPEG') 112 | 113 | string = Data(data).to_string() 114 | 115 | value = string + \ 116 | self.divided + \ 117 | output.getvalue() 118 | 119 | self.r.set(key_name, value) 120 | return True 121 | return False 122 | 123 | base_image_name = 'base_image_name' 124 | 125 | def store_base_image(self, image = ''): 126 | res = urllib2.urlopen(image) 127 | if res.code == 200: 128 | imimage = io.BytesIO(res.read()) 129 | im_instance = im.open(imimage).resize(self.image_size) 130 | output = StringIO.StringIO() 131 | im_instance \ 132 | .convert('RGB') \ 133 | .save(output, 'JPEG') 134 | 135 | return self.r.set(self.base_image_name, output.getvalue()) 136 | raise Exception('Image request fail!') 137 | 138 | def store_base_image_file(self, image = ''): 139 | im_instance = im.open(image).resize(self.image_size) 140 | output = StringIO.StringIO() 141 | im_instance \ 142 | .convert('RGB') \ 143 | .save(output, 'JPEG') 144 | 145 | self.r.set(self.base_image_name, output.getvalue()) 146 | 147 | def store_base_image_by_base64(self, image = ''): 148 | ''' 149 | : 通过 base64 储存文件 150 | ''' 151 | if 'base64,' in image: 152 | image = image.split(',')[1] 153 | image_str = cStringIO.StringIO(base64.b64decode(image)) 154 | im_instance = im.open(image_str).resize(self.image_size) 155 | output = StringIO.StringIO() 156 | im_instance \ 157 | .convert('RGB') \ 158 | .save(output, 'JPEG') 159 | 160 | self.r.set(self.base_image_name, output.getvalue()) 161 | 162 | def remove_base_image_file(self): 163 | ''' 删除缓存数据 ''' 164 | self.r.delete(self.base_image_name) 165 | 166 | 167 | def get_base_image(self): 168 | result = self.r.get(self.base_image_name) 169 | return io.BytesIO(result) 170 | 171 | def clear_db(self): 172 | ''' 173 | : 清除当前数据库 174 | ''' 175 | print 'clear db ...' 176 | self.r.flushdb() 177 | 178 | # 单个获取图片 179 | def get_image(self, key_name = None): 180 | return io.BytesIO( 181 | self.r.get(key_name) 182 | ) 183 | 184 | # 单个获取图片 185 | def get_image_with_data(self, key_name = None): 186 | data, result = self.r.get(key_name).split(self.divided) 187 | return data, io.BytesIO(result) 188 | 189 | # ---------------------------------------------------------- 190 | # 根据条件生成数据 191 | # --------------------------------------------------------- 192 | def search(self, terms = None): 193 | # 生成短名称 194 | if terms: 195 | key_name = self.__generate_key( 196 | terms.keys(), 197 | terms 198 | ) 199 | return self.r.keys('*' + key_name + '*') 200 | else: 201 | return self.r.keys('*') 202 | 203 | def get_multi(self, keys = []): 204 | if len(keys) > 0: 205 | return self.r.mget(keys) 206 | raise Exception('keys is empty!') 207 | -------------------------------------------------------------------------------- /src/service/match.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import io 4 | import json 5 | from src.lib.data import Data 6 | from src.service.feature import Feature 7 | from src.service.manage import Manage 8 | import multiprocessing as mp 9 | from multiprocessing import Pool 10 | from functools import partial 11 | 12 | def mix_hash(score_list = {}): 13 | ''' 14 | : merge image score 15 | ''' 16 | mse = 0 17 | phash = 0 18 | base = 0 19 | color = 0 20 | for detector in score_list: 21 | if detector is 'Phash': 22 | phash = score_list[detector] * 0.5 23 | if detector is 'Base': 24 | base = score_list[detector] * 9 25 | if detector is 'Mse': 26 | mse = score_list[detector] * 0.8 27 | if detector is 'Color': 28 | color = score_list[detector] * 1.1 29 | 30 | return (mse + phash + base + color) 31 | # print '---' 32 | # print phash 33 | # print base 34 | # return (base + phash) 35 | 36 | 37 | def process_match(origin_io=None, lists=None): 38 | ''' 39 | : match image by different process 40 | ''' 41 | feature = Feature() 42 | data = Data() 43 | 44 | # print lists 45 | results = [] 46 | 47 | # 直接返回 redis row data 48 | for container in lists: 49 | # 找到比对数据 50 | feature.set_byte_base_image(origin_io) 51 | 52 | # 风格数据 53 | data, image_str = container.split(Manage.divided) 54 | 55 | byte = io.BytesIO(image_str) 56 | feature.set_byte_storage_image(byte) 57 | 58 | bean = json.loads(data) 59 | result = {} 60 | # 使用算法到的 61 | # 大色块比对方案 62 | result = feature.process(None) 63 | 64 | # merge calculate score 65 | score = mix_hash(result) 66 | # 准备返回数据 67 | processed = {} 68 | 69 | for i in bean: 70 | processed[i] = bean[i] 71 | processed['score'] = score 72 | 73 | results.append(processed) 74 | return results 75 | 76 | class Match: 77 | ''' 78 | Compare Image differ I find some useful function 79 | Maybe Some function would suit for you Project 80 | ''' 81 | 82 | # 原对比文件 的 byte 属性 83 | origin_io_image = None 84 | 85 | def __init__(self, Manage = None, result_size = 10): 86 | """ 87 | 设置 管理器 88 | """ 89 | self.manage = Manage 90 | self.feature = Feature() 91 | self.data = Data() 92 | self.result_size = result_size 93 | 94 | 95 | def set_origin_image(self, image_url = ''): 96 | # 设置原图片索引 97 | if image_url is '': 98 | raise Exception('Origin image is empty') 99 | self.manage.store_base_image(image_url) 100 | 101 | def get_match_result(self, terms = None, page_size = None): 102 | ''' 103 | :param data: 104 | :return: list | None 105 | ''' 106 | # 设置原图数据 107 | self.origin_io_image = self.manage.get_base_image() 108 | 109 | limit = page_size 110 | if page_size is None: 111 | limit = self.result_size 112 | 113 | the_list = self.manage.search(terms) 114 | # 删除缓存数据 115 | if Manage.base_image_name in the_list: 116 | the_list.remove(Manage.base_image_name) 117 | 118 | for_multiprocess_list = [] 119 | for_multiprocess_list = self.manage.get_multi(the_list) 120 | 121 | print 'count result:', str(len(the_list)) 122 | 123 | results = [] 124 | core_number = mp.cpu_count() 125 | 126 | # 计算每个 cpu 需要的数量 (分化数据到每个 cpu ) 127 | n = int(round(len(for_multiprocess_list) / float(core_number))) 128 | divid_list = [for_multiprocess_list[i:i + n] for i in xrange(0, len(for_multiprocess_list), n)] 129 | 130 | 131 | # Pool的默认大小是CPU 132 | pool = Pool() 133 | func = partial(process_match, self.origin_io_image) 134 | 135 | # 并得到合并的结果集 136 | rl = pool.map(func, divid_list) 137 | pool.close() 138 | pool.join() 139 | 140 | # set two dimension to one dimension 141 | results = [item for i in rl for item in i] 142 | 143 | sortedList = sorted(results, key=lambda k: k['score']) 144 | sortedList = sortedList[:limit] 145 | item_id = 0 146 | 147 | # 处理 image 在内存里面的数据 148 | self.manage.remove_base_image_file() 149 | return sortedList 150 | -------------------------------------------------------------------------------- /src/template/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | just show test image 4 | 5 | 6 | {% include nav.html %} 7 | 8 |
 9 | rms = math.sqrt(
10 | reduce(operator.add, list(map(lambda a, b: (a - b) ** 2, h1, h2)))
11 | /
12 | len(h1)
13 | )
14 | 
15 | {% for package in package_image %} 16 | 17 | {% for i in package %} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {% end %} 30 |
base:{{ i['code'] }}mes:{{ i['code2'] }}perceptualHash:{{ i['code3'] }}color:{{ i['code4'] }}
31 |

32 | {% end %} 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | just show test image 4 | 5 | 6 | {% include nav.html %} 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/template/nav.html: -------------------------------------------------------------------------------- 1 |
2 | home 3 | demo 4 | search 5 | search color 6 | search_by_index 7 |
8 | -------------------------------------------------------------------------------- /src/template/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | search file 4 | 5 | 6 |
7 | Home 8 | 9 |
10 |

{{ name }}

11 |
12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/template/search_color_result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | search file 4 | 5 | 6 |
7 | Home 8 |
9 | 10 |

code2

11 |
    12 | {% for result in results %} 13 |
  • 14 | 15 | 16 | 17 | {{ origin_img_rgb }} // 18 | {{result['rgb']}} // 19 | {{result['score']}} // 20 | {{result['keyname']}} // 21 |
  • 22 | {% end %} 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/template/search_result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | just show test image 4 | 5 | 6 |
7 | Home 8 | 9 |
10 | 11 |

code1

12 |
    13 | {% for result in results_code1 %} 14 |
  • 15 | 16 | 17 | 18 | {{result['code1']}} // 19 |
  • 20 | {% end %} 21 |
22 | 23 |

code2

24 |
    25 | {% for result in results_code2 %} 26 |
  • 27 | 28 | 29 | 30 | {{result['code2']}} // 31 |
  • 32 | {% end %} 33 |
34 | 35 |

code3

36 |
    37 | {% for result in results_code3 %} 38 |
  • 39 | 40 | 41 | 42 | {{result['code3']}} // 43 |
  • 44 | {% end %} 45 |
46 | 47 | 48 |

color Compare

49 |
    50 | {% for result in results_code4 %} 51 |
  • 52 | 53 | 54 | 55 | {{result['code4']}} // 56 | {{result['color_package']['keyname']}} // 57 |
  • 58 | {% end %} 59 |
60 | 61 | 62 |

code_mix

63 |
    64 | {% for result in results_mix %} 65 |
  • 66 | 67 | 68 | 69 | {{result['code_mix']}} // 70 |
  • 71 | {% end %} 72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /src/template/search_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | just show test image 4 | 5 | 6 | 7 | 8 | 16 |
17 | Home 18 |
19 |
20 | {% for result in results %} 21 |
  • 22 | 23 | 24 | 25 | {{ result['name'] }} // 26 |
  • 27 | {% end %} 28 |
    29 | 30 | 31 | -------------------------------------------------------------------------------- /src/template/search_test_upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | search file 4 | 5 | 6 |
    7 | Home 8 | 9 |
    10 |

    {{ name }}

    11 |
    12 | 13 | 14 |
    15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/__init__.py -------------------------------------------------------------------------------- /tests/brick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/brick.png -------------------------------------------------------------------------------- /tests/find.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/find.png -------------------------------------------------------------------------------- /tests/finds_the_coin.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | 3 | 4 | import cv2 5 | import numpy as np 6 | from matplotlib import pyplot as plt 7 | def test_2(): 8 | # http://docs.opencv.org/3.1.0/d4/dc6/tutorial_py_template_matching.html 9 | # find 找图例子 10 | img_rgb = cv2.imread('origin_material.jpg') 11 | img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) 12 | template = cv2.imread('find.png',0) 13 | w, h = template.shape[::-1] 14 | 15 | res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED) 16 | threshold = 0.8 17 | loc = np.where( res >= threshold) 18 | for pt in zip(*loc[::-1]): 19 | cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2) 20 | cv2.imwrite('res.png',img_rgb) 21 | 22 | 23 | 24 | def test_one(): 25 | img = cv2.imread('orgin_brick.jpg',0) 26 | img2 = img.copy() 27 | img2 = img.copy() 28 | template = cv2.imread('brick.png',0) 29 | w, h = template.shape[::-1] 30 | 31 | # All the 6 methods for comparison in a list 32 | methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR', 33 | 'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED'] 34 | for meth in methods: 35 | img = img2.copy() 36 | method = eval(meth) 37 | 38 | # Apply template Matching 39 | res = cv2.matchTemplate(img,template,method) 40 | min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) 41 | 42 | # If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum 43 | if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]: 44 | top_left = min_loc 45 | else: 46 | top_left = max_loc 47 | bottom_right = (top_left[0] + w, top_left[1] + h) 48 | 49 | cv2.rectangle(img,top_left, bottom_right, 255, 2) 50 | plt.subplot(121),plt.imshow(res,cmap = 'gray') 51 | plt.title('Matching Result'), plt.xticks([]), plt.yticks([]) 52 | plt.subplot(122),plt.imshow(img,cmap = 'gray') 53 | plt.title('Detected Point'), plt.xticks([]), plt.yticks([]) 54 | plt.suptitle(meth) 55 | plt.show() 56 | 57 | def test_3(): 58 | import urllib2 59 | import io 60 | 61 | url = "http://i.gzdmc.net/images/bd2888edd41a951de1ab8cbfb33c82e2.jpg@1e_400w_400h_1c_0i_1o_90Q_1x.png"; 62 | res = urllib2.urlopen(url) 63 | # save data into Bytes 64 | imimage = io.BytesIO(res.read()) 65 | 66 | nparr = np.fromstring(imimage.read(), np.uint8) 67 | img = cv2.imdecode(nparr, cv2.CV_LOAD_IMAGE_COLOR) 68 | print dir(img) 69 | height, width, channels = img.shape 70 | print height 71 | print width 72 | print channels 73 | # img = cv2.imread() 74 | mask = np.zeros(img.shape[:2],np.uint8) 75 | 76 | bgdModel = np.zeros((1,65),np.float64) 77 | fgdModel = np.zeros((1,65),np.float64) 78 | 79 | rect = (0, 0, height - 1 ,width - 1) 80 | cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT) 81 | mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8') 82 | img = img*mask2[:,:,np.newaxis] 83 | 84 | plt.imshow(img),plt.colorbar(),plt.show() 85 | 86 | # # newmask is the mask image I manually labelled 87 | # newmask = cv2.imread('orgin_brick.jpg',0) 88 | # # whereever it is marked white (sure foreground), change mask=1 89 | # # whereever it is marked black (sure background), change mask=0 90 | # mask[newmask == 0] = 0 91 | # mask[newmask == 255] = 1 92 | # mask, bgdModel, fgdModel = cv2.grabCut(img,mask,None,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK) 93 | # mask = np.where((mask==2)|(mask==0),0,1).astype('uint8') 94 | # img = img*mask[:,:,np.newaxis] 95 | # plt.imshow(img),plt.colorbar(),plt.show() 96 | def test_find_cycle(): 97 | cap = cv2.VideoCapture(0) 98 | 99 | while(1): 100 | 101 | # Take each frame 102 | _, frame = cap.read() 103 | 104 | # Convert BGR to HSV 105 | hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) 106 | 107 | # define range of blue color in HSV 108 | lower_blue = np.array([110,50,50]) 109 | upper_blue = np.array([130,255,255]) 110 | 111 | # Threshold the HSV image to get only blue colors 112 | mask = cv2.inRange(hsv, lower_blue, upper_blue) 113 | 114 | # Bitwise-AND mask and original image 115 | res = cv2.bitwise_and(frame,frame, mask= mask) 116 | 117 | cv2.imshow('frame',frame) 118 | cv2.imshow('mask',mask) 119 | cv2.imshow('res',res) 120 | k = cv2.waitKey(5) & 0xFF 121 | if k == 27: 122 | break 123 | cv2.destroyAllWindows() 124 | test_3() 125 | -------------------------------------------------------------------------------- /tests/img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/1.jpg -------------------------------------------------------------------------------- /tests/img/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/10.jpg -------------------------------------------------------------------------------- /tests/img/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/11.jpg -------------------------------------------------------------------------------- /tests/img/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/14.jpg -------------------------------------------------------------------------------- /tests/img/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/15.jpg -------------------------------------------------------------------------------- /tests/img/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/16.jpg -------------------------------------------------------------------------------- /tests/img/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/17.jpg -------------------------------------------------------------------------------- /tests/img/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/18.jpg -------------------------------------------------------------------------------- /tests/img/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/19.jpg -------------------------------------------------------------------------------- /tests/img/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/21.jpg -------------------------------------------------------------------------------- /tests/img/22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/22.jpg -------------------------------------------------------------------------------- /tests/img/23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/23.jpg -------------------------------------------------------------------------------- /tests/img/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/3.jpg -------------------------------------------------------------------------------- /tests/img/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/4.jpg -------------------------------------------------------------------------------- /tests/img/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/5.jpg -------------------------------------------------------------------------------- /tests/img/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/6.jpg -------------------------------------------------------------------------------- /tests/img/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/7.jpg -------------------------------------------------------------------------------- /tests/img/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/8.jpg -------------------------------------------------------------------------------- /tests/img/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/img/9.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/10.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/11.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/12.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/2.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/3.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/4.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/5.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/6.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/7.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/8.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/9.jpg -------------------------------------------------------------------------------- /tests/imgs/img1/bd_logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img1/bd_logo1.png -------------------------------------------------------------------------------- /tests/imgs/img2/jp_gates_contrast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/jp_gates_contrast.png -------------------------------------------------------------------------------- /tests/imgs/img2/jp_gates_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/jp_gates_original.png -------------------------------------------------------------------------------- /tests/imgs/img2/jp_gates_photoshopped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/jp_gates_photoshopped.png -------------------------------------------------------------------------------- /tests/imgs/img2/u=1154773526,535753407&fm=21&gp=0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/u=1154773526,535753407&fm=21&gp=0.jpg -------------------------------------------------------------------------------- /tests/imgs/img2/u=2382192293,4052630182&fm=21&gp=0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/u=2382192293,4052630182&fm=21&gp=0.jpg -------------------------------------------------------------------------------- /tests/imgs/img2/u=2473462771,2519378688&fm=21&gp=0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/u=2473462771,2519378688&fm=21&gp=0.jpg -------------------------------------------------------------------------------- /tests/imgs/img2/u=2476847967,542236518&fm=21&gp=0-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/u=2476847967,542236518&fm=21&gp=0-2.jpg -------------------------------------------------------------------------------- /tests/imgs/img2/u=2476847967,542236518&fm=21&gp=0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/u=2476847967,542236518&fm=21&gp=0.jpg -------------------------------------------------------------------------------- /tests/imgs/img2/u=2741110226,162889272&fm=21&gp=0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/u=2741110226,162889272&fm=21&gp=0.jpg -------------------------------------------------------------------------------- /tests/imgs/img2/u=2755629579,2099783568&fm=21&gp=0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/u=2755629579,2099783568&fm=21&gp=0.jpg -------------------------------------------------------------------------------- /tests/imgs/img2/u=3598758,4045583727&fm=21&gp=0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img2/u=3598758,4045583727&fm=21&gp=0.jpg -------------------------------------------------------------------------------- /tests/imgs/img3/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img3/1.jpg -------------------------------------------------------------------------------- /tests/imgs/img3/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img3/2.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/1.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/10.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/2.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/3.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/4.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/562c11dfa9ec8a13836f2676f103918fa0ecc02b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/562c11dfa9ec8a13836f2676f103918fa0ecc02b.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/738b4710b912c8fc1aae2af7fb039245d68821af-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/738b4710b912c8fc1aae2af7fb039245d68821af-2.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/738b4710b912c8fc1aae2af7fb039245d68821af.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/738b4710b912c8fc1aae2af7fb039245d68821af.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/9b46f21fbe096b630a632fc60a338744ebf8ac2a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/9b46f21fbe096b630a632fc60a338744ebf8ac2a.jpg -------------------------------------------------------------------------------- /tests/imgs/img4/a8014c086e061d953a36eb027cf40ad162d9ca9c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/img4/a8014c086e061d953a36eb027cf40ad162d9ca9c.jpg -------------------------------------------------------------------------------- /tests/imgs/imgdb/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/imgdb/.gitkeep -------------------------------------------------------------------------------- /tests/imgs/storage/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/.gitkeep -------------------------------------------------------------------------------- /tests/imgs/storage/0079d7d650f8833d4d152381aa397a49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/0079d7d650f8833d4d152381aa397a49.png -------------------------------------------------------------------------------- /tests/imgs/storage/023808d2c8d421e59b44adf9d0975513.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/023808d2c8d421e59b44adf9d0975513.png -------------------------------------------------------------------------------- /tests/imgs/storage/160a31eb7ccb105f0ab9d5b7aede085b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/160a31eb7ccb105f0ab9d5b7aede085b.png -------------------------------------------------------------------------------- /tests/imgs/storage/1d7f93bf15288d660ea283ad3ad2cf36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/1d7f93bf15288d660ea283ad3ad2cf36.png -------------------------------------------------------------------------------- /tests/imgs/storage/2cb2bca1f460957b0de2e5fdd600437a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/2cb2bca1f460957b0de2e5fdd600437a.png -------------------------------------------------------------------------------- /tests/imgs/storage/3203a0be00ed57feb72a592dddf76c3e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/3203a0be00ed57feb72a592dddf76c3e.png -------------------------------------------------------------------------------- /tests/imgs/storage/3ac2c13d9a48c75a703689b3c3780e61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/3ac2c13d9a48c75a703689b3c3780e61.png -------------------------------------------------------------------------------- /tests/imgs/storage/3cf2c674b35839f1a7801ab5b11448c5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/3cf2c674b35839f1a7801ab5b11448c5.png -------------------------------------------------------------------------------- /tests/imgs/storage/3e44218e97dd14e2efeac90d012ca00c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/3e44218e97dd14e2efeac90d012ca00c.png -------------------------------------------------------------------------------- /tests/imgs/storage/4991385c86dadab8cf075e35949b6719.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/4991385c86dadab8cf075e35949b6719.png -------------------------------------------------------------------------------- /tests/imgs/storage/4a71dae033504b97bc7bbe06f0fa447f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/4a71dae033504b97bc7bbe06f0fa447f.png -------------------------------------------------------------------------------- /tests/imgs/storage/4de571fcf072849f23ef19ef4f6b9738.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/4de571fcf072849f23ef19ef4f6b9738.png -------------------------------------------------------------------------------- /tests/imgs/storage/51ffa23048d553e5f633f177aef3c990.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/51ffa23048d553e5f633f177aef3c990.png -------------------------------------------------------------------------------- /tests/imgs/storage/5f9659b5ee80cde874970ed6aa10d109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/5f9659b5ee80cde874970ed6aa10d109.png -------------------------------------------------------------------------------- /tests/imgs/storage/711be1f660213bdc5eceff97ec1f3d21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/711be1f660213bdc5eceff97ec1f3d21.png -------------------------------------------------------------------------------- /tests/imgs/storage/7d5627485e8f4ee30d8fdf8d659282c6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/7d5627485e8f4ee30d8fdf8d659282c6.png -------------------------------------------------------------------------------- /tests/imgs/storage/a27a1c9c847a7534ebc590de56e037a8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/a27a1c9c847a7534ebc590de56e037a8.png -------------------------------------------------------------------------------- /tests/imgs/storage/af3727c3075c61f1bfa434b33f01a0ed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/af3727c3075c61f1bfa434b33f01a0ed.png -------------------------------------------------------------------------------- /tests/imgs/storage/b714e06d15c85867bc785d36a8fe83d9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/b714e06d15c85867bc785d36a8fe83d9.png -------------------------------------------------------------------------------- /tests/imgs/storage/c1635f9e09bb7dcf76a6b04a51bae240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/c1635f9e09bb7dcf76a6b04a51bae240.png -------------------------------------------------------------------------------- /tests/imgs/storage/d266e6072469ca6103c5ace4bf77b6d7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/d266e6072469ca6103c5ace4bf77b6d7.png -------------------------------------------------------------------------------- /tests/imgs/storage/eb30bf991b24eb14e6083390c29bb1da.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/eb30bf991b24eb14e6083390c29bb1da.png -------------------------------------------------------------------------------- /tests/imgs/storage/ec86dff70f3634aff8a93f167f594a36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/ec86dff70f3634aff8a93f167f594a36.png -------------------------------------------------------------------------------- /tests/imgs/storage/ecc73686b4340c8a4fa5152221053705.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storage/ecc73686b4340c8a4fa5152221053705.png -------------------------------------------------------------------------------- /tests/imgs/storagetemp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storagetemp.jpg -------------------------------------------------------------------------------- /tests/imgs/storagetemp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgs/storagetemp.png -------------------------------------------------------------------------------- /tests/imgtemp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/imgtemp.jpg -------------------------------------------------------------------------------- /tests/opencv.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from matplotlib import pyplot as plt 3 | import cv2 4 | 5 | img = cv2.imread('/Users/liyang/Downloads/mQxZL0pz69ZbmfR8pSw70c7lmQgt4SI-3-fhhnxP9hUVLmIJSHmFsGWE_sifAkQv.jpg@1o.png',0) 6 | edges = cv2.Canny(img,100,200) 7 | 8 | plt.subplot(121),plt.imshow(img,cmap = 'gray') 9 | plt.title('Original Image'), plt.xticks([]), plt.yticks([]) 10 | plt.subplot(122),plt.imshow(edges,cmap = 'gray') 11 | plt.title('Edge Image'), plt.xticks([]), plt.yticks([]) 12 | 13 | plt.show() 14 | -------------------------------------------------------------------------------- /tests/orgin_brick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/orgin_brick.jpg -------------------------------------------------------------------------------- /tests/origin_material.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/origin_material.jpg -------------------------------------------------------------------------------- /tests/res.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/res.png -------------------------------------------------------------------------------- /tests/stone_ streamline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Callwoola/picture-compare/5a7748bce557ddbee350476dd5cf29f696c88324/tests/stone_ streamline.jpg -------------------------------------------------------------------------------- /tests/testOne.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | #from skimage.measure import structural_similarity as ssim 4 | from skimage.measure import structural_similarity as ssim 5 | import matplotlib.pyplot as plt 6 | import numpy as np 7 | import cv2 8 | 9 | 10 | def demo1(): 11 | from PIL import Image 12 | import ImageChops 13 | 14 | path = "D:/code/image/image/" 15 | im1 = Image.open(path + "1.jpg") 16 | im2 = Image.open(path + "2.jpg") 17 | 18 | diff = ImageChops.difference(im2, im1) 19 | print diff 20 | 21 | im1.show() 22 | im2.show() 23 | diff.show() 24 | 25 | 26 | def mse(imageA, imageB): 27 | # the 'Mean Squared Error' between the two images is the 28 | # sum of the squared difference between the two images; 29 | # NOTE: the two images must have the same dimension 30 | err = np.sum((imageA.astype("float") - imageB.astype("float")) ** 2) 31 | err /= float(imageA.shape[0] * imageA.shape[1]) 32 | 33 | # return the MSE, the lower the error, the more "similar" 34 | # the two images are 35 | return err 36 | 37 | 38 | def compare_images(imageA, imageB, title): 39 | # compute the mean squared error and structural similarity 40 | # index for the images 41 | m = mse(imageA, imageB) 42 | print title 43 | print m 44 | print "-----" 45 | s = ssim(imageA, imageB) 46 | 47 | # setup the figure 48 | fig = plt.figure(title) 49 | plt.suptitle("MSE: %.2f, SSIM: %.2f" % (m, s)) 50 | #plt.suptitle("MSE: %.2f, SSIM: ??" % (m)) 51 | # show first image 52 | ax = fig.add_subplot(1, 2, 1) 53 | plt.imshow(imageA, cmap=plt.cm.gray) 54 | plt.axis("off") 55 | 56 | # show the second image 57 | ax = fig.add_subplot(1, 2, 2) 58 | plt.imshow(imageB, cmap=plt.cm.gray) 59 | plt.axis("off") 60 | 61 | # show the images 62 | plt.show() 63 | 64 | # load the images -- the original, the original + contrast, 65 | # and the original + photoshop 66 | 67 | 68 | 69 | original = cv2.imread("img/img2/jp_gates_contrast.png") 70 | contrast = cv2.imread("img/img2/jp_gates_original.png") 71 | shopped = cv2.imread("img/img2/jp_gates_photoshopped.png") 72 | 73 | # convert the images to grayscale 74 | original = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY) 75 | contrast = cv2.cvtColor(contrast, cv2.COLOR_BGR2GRAY) 76 | shopped = cv2.cvtColor(shopped, cv2.COLOR_BGR2GRAY) 77 | 78 | # initialize the figure 79 | fig = plt.figure("Images") 80 | images = ("Original", original), ("Contrast", contrast), ("Photoshopped", shopped) 81 | #images = ("Original", original), ("Contrast", contrast) 82 | # loop over the images 83 | for (i, (name, image)) in enumerate(images): 84 | # show the image 85 | ax = fig.add_subplot(1, 3, i + 1) 86 | ax.set_title(name) 87 | plt.imshow(image, cmap=plt.cm.gray) 88 | plt.axis("off") 89 | 90 | # show the figure 91 | plt.show() 92 | 93 | # compare the images 94 | compare_images(original, original, "Original vs. Original") 95 | compare_images(original, contrast, "Original vs. Contrast") 96 | compare_images(original, shopped, "Original vs. Photoshopped") 97 | -------------------------------------------------------------------------------- /tests/testThree.py: -------------------------------------------------------------------------------- 1 | def first(): 2 | import PIL 3 | from PIL import Image 4 | from matplotlib import pyplot as plt 5 | 6 | im = Image.open('../img/img1/1.jpg') 7 | w, h = im.size 8 | colors = im.getcolors(w * h) 9 | 10 | def hexencode(rgb): 11 | r = rgb[0] 12 | g = rgb[1] 13 | b = rgb[2] 14 | return '#%02x%02x%02x' % (r, g, b) 15 | 16 | def getColor(im): 17 | im.size 18 | 19 | for idx, c in enumerate(colors): 20 | plt.bar(idx, c[0], color=hexencode(c[1])) 21 | for idx, c in enumerate(colors): 22 | plt.bar(idx, c[0], color=hexencode(c[1]), edgecolor=hexencode(c[1])) 23 | 24 | plt.show() 25 | 26 | 27 | def main(): 28 | import cv2 29 | import matplotlib.pyplot as plt 30 | 31 | def show_histogram(im): 32 | """ Function to display image histogram. 33 | Supports single and three channel images. """ 34 | 35 | if im.ndim == 2: 36 | # Input image is single channel 37 | plt.hist(im.flatten(), 256, range=(0, 250), fc='k') 38 | plt.show() 39 | 40 | elif im.ndim == 3: 41 | # Input image is three channels 42 | fig = plt.figure() 43 | fig.add_subplot(311) 44 | plt.hist(im[..., 0].flatten(), 256, range=(0, 250), fc='b') 45 | fig.add_subplot(312) 46 | plt.hist(im[..., 1].flatten(), 256, range=(0, 250), fc='g') 47 | fig.add_subplot(313) 48 | plt.hist(im[..., 2].flatten(), 256, range=(0, 250), fc='r') 49 | plt.show() 50 | 51 | if __name__ == '__main__': 52 | im = cv2.imread('../img/img1/1.jpg') 53 | if not (im is None): 54 | show_histogram(im) 55 | 56 | 57 | def two(): 58 | import cv2 59 | import matplotlib.pyplot as plt 60 | 61 | im = cv2.imread('../img/img1/1.jpg') 62 | plt.hist(im.flatten(), 256, range=(0, 255)) 63 | plt.hist(im[..., 0].flatten(), 256, range=(0, 250), fc='b') 64 | plt.show() 65 | 66 | 67 | def test(): 68 | from PIL import Image 69 | import cv2 70 | 71 | path = '../img/img1/1.jpg' 72 | im = Image.open(path) 73 | im2 = cv2.imread(path)[..., 0].flatten() 74 | print len(im2) 75 | array = im.convert("RGB").histogram() 76 | print len(array) 77 | 78 | 79 | def major(): 80 | from PIL import Image 81 | 82 | im = Image.open("../img/img1/1.jpg") 83 | return max(im.getcolors(im.size[0] * im.size[1])) 84 | 85 | 86 | def getRgba(path): 87 | from PIL import Image 88 | r, g, b, a = Image.open(path).convert('RGBA').resize((1, 1)).getcolors()[0][1] 89 | return "rgba(%d, %d, %d, %d)" % (r, g, b, a) 90 | 91 | 92 | if __name__ == '__main__': 93 | # two() 94 | # test() 95 | print getRgba("../img/img4/1.jpg") 96 | -------------------------------------------------------------------------------- /tests/testTwo.py: -------------------------------------------------------------------------------- 1 | import scipy as sp 2 | from scipy.misc import imread 3 | from scipy.signal.signaltools import correlate2d as c2d 4 | 5 | 6 | def get(i): 7 | # get JPG image as Scipy array, RGB (3 layer) 8 | print i 9 | data = imread(i) 10 | # convert to grey-scale using W3C luminance calc 11 | data = sp.inner(data, [299, 587, 114]) / 1000.0 12 | # normalize per http://en.wikipedia.org/wiki/Cross-correlation 13 | return (data - data.mean()) / data.std() 14 | 15 | 16 | im1 = get("./img/1.jpg") 17 | im2 = get("./img/3.jpg") 18 | im3 = get("./img/4.jpg") 19 | 20 | c11 = c2d(im1, im1, mode='same') # baseline 21 | print 'starting...' 22 | print c11.max() 23 | 24 | exit() 25 | c12 = c2d(im1, im2, mode='same') 26 | c13 = c2d(im1, im3, mode='same') 27 | c23 = c2d(im2, im3, mode='same') 28 | print c11.max(), c12.max(), c13.max(), c23.max() 29 | -------------------------------------------------------------------------------- /tests/test_base.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import json 3 | import StringIO 4 | # from src.module import json_module 5 | # from src import config 6 | # from src.adapter import db 7 | import hashlib, time, os 8 | 9 | import unittest 10 | 11 | from subprocess import call 12 | 13 | 14 | class TestBaseSearch(unittest.TestCase): 15 | # def nottest_first(self): 16 | # ''' there is processing img and return img list ''' 17 | # file_path = '/Users/liyang/Code/picture-compare/img/img1/1.jpg' 18 | # tmp_name = file_path 19 | # terms = [] 20 | # from src.service.compare import Compare 21 | # compareDict = Compare().setCompareImage(tmp_name, terms) 22 | # for item in compareDict: 23 | # # print call(['imgo '+item['url']]) 24 | # print call(['id -un']) 25 | # self.assertTrue(len(compareDict) > 0) 26 | def test_base(self): 27 | self.assertEqual(True, True) 28 | 29 | 30 | def test_module(self): 31 | print '\n\nstart' 32 | from src.service.compare import Compare 33 | file_path = '/Users/liyang/Code/picture-compare/img/img1/1.jpg' 34 | compareDict = Compare().start(file_path) 35 | self.assertEqual(True, True) 36 | 37 | def main(): 38 | suite = unittest.TestLoader().loadTestsFromTestCase(TestBaseSearch) 39 | unittest.TextTestRunner(verbosity=2).run(suite) 40 | -------------------------------------------------------------------------------- /tests/test_camera.py: -------------------------------------------------------------------------------- 1 | # o 2 | 3 | import cv2 4 | import sys 5 | 6 | cascPath = sys.argv[1] 7 | faceCascade = cv2.CascadeClassifier(cascPath) 8 | 9 | video_capture = cv2.VideoCapture(0) 10 | 11 | # 捕捉人脸 12 | while True: 13 | # Capture frame-by-frame 14 | ret, frame = video_capture.read() 15 | 16 | gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 17 | 18 | faces = faceCascade.detectMultiScale( 19 | gray, 20 | scaleFactor=1.1, 21 | minNeighbors=5, 22 | minSize=(30, 30), 23 | flags=cv2.cv.CV_HAAR_SCALE_IMAGE 24 | ) 25 | 26 | # Draw a rectangle around the faces 27 | for (x, y, w, h) in faces: 28 | cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) 29 | 30 | # Display the resulting frame 31 | cv2.imshow('Video', frame) 32 | 33 | if cv2.waitKey(1) & 0xFF == ord('q'): 34 | break 35 | 36 | # When everything is done, release the capture 37 | video_capture.release() 38 | cv2.destroyAllWindows() 39 | -------------------------------------------------------------------------------- /tests/test_index.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import os 3 | import json 4 | import requests 5 | 6 | class Index: 7 | target_url = "http://120.25.56.146:1565/_index" 8 | def index(self.id, name, image_url): 9 | post = { 10 | 'query' : { 11 | 'id' : id, 12 | 'name' : name, 13 | 'url' : image_url, 14 | 'search' : { 15 | }, 16 | 'data': { 17 | 'origin_url' image_url:, 18 | }, 19 | } 20 | } 21 | payload = payload % (id, name, image_url, image_url) 22 | headers = { 23 | 'content-type': "application/json", 24 | } 25 | response = requests.request( 26 | "POST", 27 | self.target_url, 28 | data=payload, 29 | headers=headers 30 | ) 31 | print(response.text) 32 | 33 | if __name__ == '__main__': 34 | # 请把本地图片上传到在线服务器 35 | # 并把对应内容填写到下面的变量 36 | # python main.py 37 | online_image_repo = 'http://online.app/images/' 38 | local_image_repo = '/Users/n/image_repo' 39 | img_list = os.listdir(path) 40 | capsule = Index() 41 | for i in img_list: 42 | capsule.index(img_list.index(i), i, online_repo_url + i) 43 | -------------------------------------------------------------------------------- /tests/test_interceptors.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import logging 3 | import logging.config 4 | import tornado.httpserver 5 | import tornado.ioloop 6 | import tornado.web 7 | 8 | log = logging.getLogger("root") 9 | 10 | 11 | def interceptor(func): 12 | """ 13 | This is a class decorator which is helpful in configuring 14 | one or more interceptors which are able to intercept, inspect, 15 | process and approve or reject further processing of the request 16 | """ 17 | 18 | def classwrapper(cls): 19 | def wrapper(old): 20 | def inner(self, transforms, *args, **kwargs): 21 | print self.request 22 | print old 23 | return old(self, transforms, *args, **kwargs) 24 | return inner 25 | cls._execute = wrapper(cls._execute) 26 | return cls 27 | return classwrapper 28 | 29 | 30 | @interceptor("func-") 31 | class MainHandler(tornado.web.RequestHandler): 32 | def get(self): 33 | self.write("Hello, interceptor") 34 | 35 | 36 | application = tornado.web.Application([ 37 | (r"/", MainHandler), 38 | ]) 39 | 40 | if __name__ == "__main__": 41 | http_server = tornado.httpserver.HTTPServer(application) 42 | http_server.listen(3232) 43 | tornado.ioloop.IOLoop.instance().start() 44 | 45 | -------------------------------------------------------------------------------- /tests/test_queue.py: -------------------------------------------------------------------------------- 1 | import HTMLParser 2 | import time 3 | import urlparse 4 | from datetime import timedelta 5 | 6 | from tornado import httpclient, gen, ioloop, queues 7 | 8 | base_url = 'http://www.tornadoweb.org/en/stable/' 9 | concurrency = 10 10 | 11 | 12 | @gen.coroutine 13 | def get_links_from_url(url): 14 | """Download the page at `url` and parse it for links. 15 | 16 | Returned links have had the fragment after `#` removed, and have been made 17 | absolute so, e.g. the URL 'gen.html#tornado.gen.coroutine' becomes 18 | 'http://www.tornadoweb.org/en/stable/gen.html'. 19 | """ 20 | try: 21 | response = yield httpclient.AsyncHTTPClient().fetch(url) 22 | print('fetched %s' % url) 23 | urls = [urlparse.urljoin(url, remove_fragment(new_url)) 24 | for new_url in get_links(response.body)] 25 | except Exception as e: 26 | print('Exception: %s %s' % (e, url)) 27 | raise gen.Return([]) 28 | 29 | raise gen.Return(urls) 30 | 31 | 32 | def remove_fragment(url): 33 | scheme, netloc, url, params, query, fragment = urlparse.urlparse(url) 34 | return urlparse.urlunparse((scheme, netloc, url, params, query, '')) 35 | 36 | 37 | def get_links(html): 38 | class URLSeeker(HTMLParser.HTMLParser): 39 | def __init__(self): 40 | HTMLParser.HTMLParser.__init__(self) 41 | self.urls = [] 42 | 43 | def handle_starttag(self, tag, attrs): 44 | href = dict(attrs).get('href') 45 | if href and tag == 'a': 46 | self.urls.append(href) 47 | 48 | url_seeker = URLSeeker() 49 | url_seeker.feed(html) 50 | return url_seeker.urls 51 | 52 | 53 | @gen.coroutine 54 | def main(): 55 | q = queues.Queue() 56 | start = time.time() 57 | fetching, fetched = set(), set() 58 | 59 | @gen.coroutine 60 | def fetch_url(): 61 | current_url = yield q.get() 62 | try: 63 | if current_url in fetching: 64 | return 65 | 66 | print('fetching %s' % current_url) 67 | fetching.add(current_url) 68 | urls = yield get_links_from_url(current_url) 69 | fetched.add(current_url) 70 | 71 | for new_url in urls: 72 | # Only follow links beneath the base URL 73 | if new_url.startswith(base_url): 74 | yield q.put(new_url) 75 | 76 | finally: 77 | q.task_done() 78 | 79 | @gen.coroutine 80 | def worker(): 81 | while True: 82 | yield fetch_url() 83 | 84 | q.put(base_url) 85 | 86 | # Start workers, then wait for the work queue to be empty. 87 | for _ in range(concurrency): 88 | worker() 89 | yield q.join(timeout=timedelta(seconds=300)) 90 | assert fetching == fetched 91 | print('Done in %d seconds, fetched %s URLs.' % ( 92 | time.time() - start, len(fetched))) 93 | 94 | 95 | if __name__ == '__main__': 96 | import logging 97 | logging.basicConfig() 98 | io_loop = ioloop.IOLoop.current() 99 | io_loop.run_sync(main) -------------------------------------------------------------------------------- /tests/test_time.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | class Testsample1(): 3 | """This is a dummy test file used for testing testimony""" 4 | 5 | def test_picture_compare(self): 6 | # Code to perform the test 7 | pass 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------