diff --git a/.gitignore b/.gitignore index 091919c..83ed8e8 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,11 @@ instance/ # Sphinx documentation docs/_build/ +# py.test +.cache/* +.pytest_cache/* +.pytest_config + # PyBuilder target/ diff --git a/Dockerfile b/Dockerfile index 3514060..5eddc8c 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,55 +1,46 @@ # TensorPy Docker Image -FROM ubuntu:15.10 +FROM ubuntu:17.10 -#======================================= -# Install Python and Basic Python Tools -#======================================= -RUN apt-get update && apt-get install -y python python-pip python-setuptools \ - python-dev python-distribute python-virtualenv +#================================ +# Update apt-get package sources +#================================ +RUN apt-get update -#========================================= -# Install Bash Command Line Tools and Git -#========================================= +#================================================== +# Install Bash Command Line Tools, Python, and Git +#================================================== RUN apt-get -qy --no-install-recommends install \ + python \ + python-dev \ + python-pip \ + python-distribute \ + python-virtualenv \ + python-setuptools \ sudo \ unzip \ wget \ curl \ + libxi6 \ + libgconf-2-4 \ vim \ git-core \ && rm -rf /var/lib/apt/lists/* -#======================================== -# Add normal user with passwordless sudo -#======================================== -RUN sudo useradd seluser --shell /bin/bash --create-home \ - && sudo usermod -a -G sudo seluser \ - && echo 'ALL ALL = (ALL) NOPASSWD: ALL' >> /etc/sudoers - -#============================== -# Locale and encoding settings -#============================== -ENV LANGUAGE en_US.UTF-8 -ENV LANG ${LANGUAGE} -RUN locale-gen ${LANGUAGE} \ - && dpkg-reconfigure --frontend noninteractive locales - #============================== # Set up TensorFlow / TensorPy #============================== -COPY third_party/docker/docker_install.sh /TensorPy/docker_install.sh +COPY install.sh /TensorPy/install.sh COPY requirements.txt /TensorPy/ COPY setup.py /TensorPy/ COPY tensorpy /TensorPy/tensorpy/ COPY examples /TensorPy/examples/ -COPY third_party/docker /TensorPy/third_party/docker/ -COPY third_party/docker/run_docker_test.sh /TensorPy/ -RUN cd /TensorPy && ls && ./third_party/docker/docker_install.sh +COPY integrations/docker/run_docker_test.sh /TensorPy/ +RUN cd /TensorPy && ls && ./install.sh RUN cd /TensorPy && pip install -r requirements.txt #=================== # Create entrypoint #=================== -COPY third_party/docker/docker-entrypoint.sh / +COPY integrations/docker/docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/bin/bash"] diff --git a/LICENSE b/LICENSE index 3ed9a1a..107c651 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2017 Michael Mintz +Copyright (c) 2018 Michael Mintz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2eaa7de..56b21ec 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ![](http://cdn2.hubspot.net/hubfs/100006/images/tensorpy_logo_4_p.png "TensorPy") # TensorPy -[![pypi](https://img.shields.io/pypi/v/tensorpy.svg)](https://pypi.python.org/pypi/tensorpy) [![GitHub stars](https://img.shields.io/github/stars/TensorPy/TensorPy.svg "GitHub stars")](https://github.com/TensorPy/TensorPy/stargazers) [![Python version](https://img.shields.io/badge/python-2.7-22AADD.svg "Python version")](https://docs.python.org/2/) [![MIT License](http://img.shields.io/badge/license-MIT-22BBCC.svg "MIT License")](https://github.com/TensorPy/TensorPy/blob/master/LICENSE) [![Join the chat at https://gitter.im/TensorPy/Lobby](https://badges.gitter.im/TensorPy/TensorPy.svg)](https://gitter.im/TensorPy/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![pypi](https://img.shields.io/pypi/v/tensorpy.svg)](https://pypi.python.org/pypi/tensorpy) [![Python version](https://img.shields.io/badge/python-3.5,_3.6,_3.7-22AADD.svg "Python version")](https://docs.python.org/2/) [![Join the chat at https://gitter.im/TensorPy/Lobby](https://badges.gitter.im/TensorPy/TensorPy.svg)](https://gitter.im/TensorPy/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) **Easy Image Classification with TensorFlow** @@ -9,16 +9,19 @@ (**[Watch the 2-minute tutorial on YouTube](https://www.youtube.com/watch?v=lVtzaHcUE7Q)**) -Now runs **much faster** since video released! +### Requirements: -You can use TensorPy to classify images by simply passing a URL on the command line, or by using TensorPy in your Python programs. **[TensorFlow](https://www.tensorflow.org/)** does all the image-recognition work. TensorPy also simplifies TensorFlow installation by automating several setup steps into a single script (See **[install.sh](https://github.com/TensorPy/TensorPy/blob/master/install.sh)** for details). +* A Mac or Linux machine +* Python 3.5, 3.6, or 3.7 + +You can use TensorPy to classify images by simply passing a URL on the command line, or by using TensorPy in your Python programs. **[TensorFlow](https://www.tensorflow.org/)** does all the real work. TensorPy also simplifies TensorFlow installation by automating several setup steps into a single script (See **[install.sh](https://github.com/TensorPy/TensorPy/blob/master/install.sh)** for details). (Read **[how_tensorpy_works](https://github.com/TensorPy/TensorPy/blob/master/help_docs/how_tensorpy_works.md)** for a detailed explanation of how TensorPy works.) ## Setup Steps for Mac & Ubuntu/Linux -(**Windows & Docker users**: See the **[Docker ReadMe](https://github.com/TensorPy/TensorPy/blob/master/third_party/docker/ReadMe.md)** for running on a Docker machine. Windows requires Docker to run TensorFlow.) +(**Windows & Docker users**: See the **[Docker ReadMe](https://github.com/TensorPy/TensorPy/blob/master/integrations/docker/ReadMe.md)** for running on a Docker machine. Windows requires Docker to run TensorFlow.) #### **Step 1:** Create and activate a virtual environment named "tensorpy" @@ -33,7 +36,7 @@ cd TensorPy #### **Step 3:** Install TensorPy, TensorFlow, and ImageNet/Inception -The **[install.sh](https://github.com/TensorPy/TensorPy/blob/master/install.sh)** script installs everything you need: +Use **[install.sh](https://github.com/TensorPy/TensorPy/blob/master/install.sh)** to install everything you need. ```bash ./install.sh @@ -41,6 +44,8 @@ The **[install.sh](https://github.com/TensorPy/TensorPy/blob/master/install.sh)* #### **Step 4:** Run the examples +(NOTE: Run times may vary depending on your Internet connection and computer's CPU speed.) + Classify a single image from a URL: ```bash @@ -55,9 +60,43 @@ classify "https://github.com/TensorPy/TensorPy/tree/master/examples/images" Classify a single image URL from a Python script: (See **[test_python_classify.py](https://github.com/TensorPy/TensorPy/blob/master/examples/test_python_classify.py)** for details.) +```bash +python examples/test_python_classify.py +``` + +Classify an image from a local file path: + +```bash +classify examples/images/cat_animal.jpg +``` + +Classify all images from a local folder: + +```bash +classify examples/images/ +``` + +#### **Examples in Python programs:** + +Classify an image from a local file path using a Python script: (See **[test_python_file_classify.py](https://github.com/TensorPy/TensorPy/blob/master/examples/test_python_file_classify.py)** for details.) + +```bash +cd examples +python test_python_file_classify.py +``` + +Classify all images in a local folder using a Python script (Output = LIST): (See **[test_python_folder_classify.py](https://github.com/TensorPy/TensorPy/blob/master/examples/test_python_folder_classify.py)** for details.) + +```bash +cd examples +python test_python_folder_classify.py +``` + +Classify all images in a local folder using a Python script (Output = DICTIONARY): (See **[test_python_folder_classify_dict.py](https://github.com/TensorPy/TensorPy/blob/master/examples/test_python_folder_classify_dict.py)** for details.) + ```bash cd examples -python test_python_classify.py +python test_python_folder_classify_dict.py ``` ____________ diff --git a/examples/ReadMe.md b/examples/ReadMe.md index 541b5c0..17f2efa 100755 --- a/examples/ReadMe.md +++ b/examples/ReadMe.md @@ -1,6 +1,6 @@ ## Running TensorPy Examples -(NOTE: If you didn't install TensorFlow and TensorPy properly from the README instructions, these scripts won't work.) +(NOTE: If you didn't install TensorFlow and TensorPy properly from the [README](https://github.com/TensorPy/TensorPy/blob/master/README.md) instructions, these scripts won't work.) ```bash ./test_classify_image.sh @@ -14,4 +14,6 @@ You can also perform classifications from Python scripts: python test_python_classify.py ``` -(NOTE: If you see any ``*.pyc`` files appear as you run tests, that is compiled bytecode, which is a natural result of running Python code.) +(NOTE: If you see any ``*.pyc`` files appear as you run Python scripts, that is compiled bytecode, which is a natural result of running Python code.) + +For more examples, see Step 4 of the [README](https://github.com/TensorPy/TensorPy/blob/master/README.md). diff --git a/examples/test_python_file_classify.py b/examples/test_python_file_classify.py new file mode 100644 index 0000000..8523edf --- /dev/null +++ b/examples/test_python_file_classify.py @@ -0,0 +1,4 @@ +from tensorpy import image_base + +result = image_base.classify_local_image("images/cat_animal.jpg") +print("\nBest match classification:\n%s\n" % result) diff --git a/examples/test_python_folder_classify.py b/examples/test_python_folder_classify.py new file mode 100644 index 0000000..c3c6dc2 --- /dev/null +++ b/examples/test_python_folder_classify.py @@ -0,0 +1,6 @@ +from tensorpy import image_base + +classifications = image_base.classify_folder_images('./images') +print("*** Displaying Image Classification Results as a list: ***") +for classification in classifications: + print(classification) diff --git a/examples/test_python_folder_classify_dict.py b/examples/test_python_folder_classify_dict.py new file mode 100644 index 0000000..5119129 --- /dev/null +++ b/examples/test_python_folder_classify_dict.py @@ -0,0 +1,5 @@ +from tensorpy import image_base + +dictionary = image_base.classify_folder_images('./images', return_dict=True) +print("*** Displaying Image Classification Results as a dictionary: ***") +print(dictionary) diff --git a/help_docs/how_tensorpy_works.md b/help_docs/how_tensorpy_works.md index b01300e..0d76f96 100755 --- a/help_docs/how_tensorpy_works.md +++ b/help_docs/how_tensorpy_works.md @@ -1,5 +1,7 @@ ### How TensorPy Works (Detailed Explanation) +(NOTE: Run times may vary depending on your Internet connection and computer's CPU speed.) + Once TensorPy is installed with **[install.sh](https://github.com/TensorPy/TensorPy/blob/master/install.sh)**, a command called ``classify`` is added to your command line, which takes a URL as input. When called, TensorPy determines if that URL links to an image or a web image. If it's an image, TensorPy downloads the image to a new folder called ``downloads_folder``. From there, the image is converted to a JPEG. Then, the image is fed to the local **[tensorpy/classify_image.py](https://github.com/TensorPy/TensorPy/blob/master/tensorpy/classify_image.py)** which tells TensorFlow to use the ImageNet Inception database to classify the image and print out the result. If the ``classify`` command is called with a web page as input, then all images on that page (up to the limit defined in **[tensorpy/settings.py](https://github.com/TensorPy/TensorPy/blob/master/tensorpy/settings.py)**) will get downloaded to ``downloads_folder``, where TensorPy does the work listed above per image, with the exception of images smaller than the minimum size defined in tensorpy/settings.py (currently 50x50 pixels). Images that are too small will be skipped because results have shown that icons and other tiny images get extremely poor classification results. diff --git a/help_docs/pip_installation.md b/help_docs/pip_installation.md index 158a808..6ecdaad 100755 --- a/help_docs/pip_installation.md +++ b/help_docs/pip_installation.md @@ -1,13 +1,19 @@ ### Pip Installation Instructions -Ubuntu/Linux +Ubuntu/Linux: ```bash sudo apt-get install python-pip python-dev ``` -Mac OS X +Mac OS X: ```bash sudo easy_install pip ``` + +OR + +```bash +python -m pip install -U pip +``` diff --git a/help_docs/virtualenv_instructions.md b/help_docs/virtualenv_instructions.md index 2452237..692f620 100755 --- a/help_docs/virtualenv_instructions.md +++ b/help_docs/virtualenv_instructions.md @@ -1,15 +1,15 @@ ### Virtual Environment Setup Tutorial -First: +First, install virtualenv / virtualenvwrapper: ```bash -sudo pip install virtualenv -sudo pip install virtualenvwrapper +python -m pip install virtualenv +python -m pip install virtualenvwrapper export WORKON_HOME=$HOME/.virtualenvs source /usr/local/bin/virtualenvwrapper.sh ``` -Next, follow the command depending on the system you're using: +If you add ``source /usr/local/bin/virtualenvwrapper.sh`` to your local bash file (``~/.bash_profile`` on Mac, ``~/.bashrc`` on Linux), virtualenvwrapper commands will be available to you whenever you open a new command prompt. You can then use the following commands to make those changes active. Mac users: ``source ~/.bash_profile`` Linux users: ``source ~/.bashrc`` @@ -28,7 +28,6 @@ deactivate You can always jump back in later: -(If you're using ``virtualenvwrapper``): ```bash workon tensorpy ``` diff --git a/install.sh b/install.sh index 4417369..2062c23 100755 --- a/install.sh +++ b/install.sh @@ -1,20 +1,19 @@ # Installs TensorPy, TensorFlow, ImageNet, and required dependancies -pip install --upgrade pip -echo "Installing TensorPy..." -pip install -r requirements.txt -python setup.py install +python -m pip install --upgrade pip +echo "Installing TensorPy:" +pip install -r requirements.txt --upgrade +python setup.py develop value="$(uname)" -if [ $value == "Linux" ] +if [ $value = "Linux" ] then echo "Initializing TensorFlow setup on a Linux machine..." - export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.1.0-cp27-none-linux_x86_64.whl + export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.14.0-cp27-none-linux_x86_64.whl pip install --ignore-installed --upgrade $TF_BINARY_URL -elif [ $value == "Darwin" ] +elif [ $value = "Darwin" ] then echo "Initializing TensorFlow setup on a MAC..." - export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-1.1.0-py2-none-any.whl - pip install --ignore-installed --upgrade $TF_BINARY_URL + pip install --upgrade tensorflow else echo "Incompatible machine for TensorFlow. Exiting script..." fi diff --git a/third_party/ReadMe.md b/integrations/ReadMe.md similarity index 59% rename from third_party/ReadMe.md rename to integrations/ReadMe.md index 287ae25..75f693f 100755 --- a/third_party/ReadMe.md +++ b/integrations/ReadMe.md @@ -1,3 +1,3 @@ -## Third-Party / Integrations +## Integrations This folder contains TensorPy integrations diff --git a/third_party/docker/ReadMe.md b/integrations/docker/ReadMe.md similarity index 100% rename from third_party/docker/ReadMe.md rename to integrations/docker/ReadMe.md diff --git a/third_party/docker/docker-entrypoint.sh b/integrations/docker/docker-entrypoint.sh similarity index 100% rename from third_party/docker/docker-entrypoint.sh rename to integrations/docker/docker-entrypoint.sh diff --git a/third_party/docker/run_docker_test.sh b/integrations/docker/run_docker_test.sh similarity index 100% rename from third_party/docker/run_docker_test.sh rename to integrations/docker/run_docker_test.sh diff --git a/requirements.txt b/requirements.txt index fb2f6bf..5f4fae2 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ -requests>=2.13.0 -six>=1.10.0 -Pillow>=4.1.1 -BeautifulSoup>=3.2.1 --e . +tensorflow==1.15.4 +pip>=20.2.3 +six>=1.15.0 +requests>=2.24.0 +Pillow==7.2.0 +beautifulsoup4==4.9.2 diff --git a/setup.cfg b/setup.cfg new file mode 100755 index 0000000..cb069cd --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[bdist_wheel] +# TensorPy works for both Python 2.7 and Python 3 +universal=1 diff --git a/setup.py b/setup.py index 9b5ad5b..863853e 100644 --- a/setup.py +++ b/setup.py @@ -5,21 +5,72 @@ """ from setuptools import setup, find_packages # noqa +import os +import sys + + +this_directory = os.path.abspath(os.path.dirname(__file__)) +long_description = None +try: + with open(os.path.join(this_directory, 'README.md'), 'rb') as f: + long_description = f.read().decode('utf-8') +except IOError: + long_description = 'Easy Image Classification with TensorFlow!' + +if sys.argv[-1] == 'publish': + reply = None + input_method = input + if not sys.version_info[0] >= 3: + input_method = raw_input # noqa + reply = str(input_method( + '>>> Confirm release PUBLISH to PyPI? (yes/no): ')).lower().strip() + if reply == 'yes': + print("\n*** Checking code health with flake8:\n") + flake8_status = os.system("flake8 --exclude=temp") + if flake8_status != 0: + print("\nWARNING! Fix flake8 issues before publishing to PyPI!\n") + sys.exit() + else: + print("*** No flake8 issues detected. Continuing...") + print("\n*** Rebuilding distribution packages: ***\n") + os.system('rm -f dist/*.egg; rm -f dist/*.tar.gz; rm -f dist/*.whl') + os.system('python setup.py sdist bdist_wheel') # Create new tar/wheel + print("\n*** Installing twine: *** (Required for PyPI uploads)\n") + os.system("python -m pip install --upgrade 'twine>=1.15.0'") + print("\n*** Installing tqdm: *** (Required for PyPI uploads)\n") + os.system("python -m pip install --upgrade 'tqdm>=4.49.0'") + print("\n*** Publishing The Release to PyPI: ***\n") + os.system('python -m twine upload dist/*') # Requires ~/.pypirc Keys + print("\n*** The Release was PUBLISHED SUCCESSFULLY to PyPI! :) ***\n") + else: + print("\n>>> The Release was NOT PUBLISHED to PyPI! <<<\n") + sys.exit() setup( name='tensorpy', - version='1.0.14', - url='http://tensorpy.com', + version='1.6.1', + description='Easy Image Classification with TensorFlow!', + long_description=long_description, + long_description_content_type='text/markdown', + url='https://github.com/TensorPy/TensorPy', + platforms=["Linux", "Unix", "Mac OS-X"], author='Michael Mintz', - author_email='@mintzworld', + author_email='mdmintz@gmail.com', maintainer='Michael Mintz', - description='Easy Image Classification with TensorFlow!', - license='The MIT License', + license="MIT", + classifiers=[ + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + ], + python_requires='>=3.5, <3.8', install_requires=[ - 'requests>=2.13.0', - 'six>=1.10.0', - 'Pillow>=4.1.1', - 'BeautifulSoup>=3.2.1', + 'tensorflow==1.15.4', + 'pip>=20.2.3', + 'six>=1.15.0', + 'requests>=2.24.0', + 'Pillow==7.2.0', + 'beautifulsoup4==4.9.2', ], packages=['tensorpy'], entry_points={ diff --git a/tensorpy/classify.py b/tensorpy/classify.py index 8da263f..518e97e 100644 --- a/tensorpy/classify.py +++ b/tensorpy/classify.py @@ -3,6 +3,7 @@ import threading import uuid from multiprocessing.dummy import Pool as ThreadPool +from os.path import isdir, isfile from tensorpy import classify_image from tensorpy import settings from tensorpy import image_base @@ -21,7 +22,7 @@ def download_and_classify_image(image_url): downloads_folder = settings.DOWNLOADS_FOLDER # Prevent file conflicts by using unique identifiers - hex_name = 'temp_image_%s' % uuid.uuid4().get_hex() + hex_name = 'temp_image_%s' % uuid.uuid4().hex hex_name_png = hex_name + '.png' hex_name_jpg = hex_name + '.jpg' @@ -64,19 +65,30 @@ def main(): expected_arg = "[A valid PAGE_URL or IMAGE_URL]" num_args = len(sys.argv) if num_args < 2 or num_args > 2: - print "\n* INVALID RUN COMMAND! * Usage:" - print "classify %s\n" % expected_arg + print("\n* INVALID RUN COMMAND! * Usage:") + print("classify %s\n" % expected_arg) elif num_args == 2: url = sys.argv[1] valid_url = web_core.is_valid_url(url) if not valid_url: - raise Exception("Error: %s is not a valid URL!" % url) + file_path = url + if isfile(file_path): + best_guess = image_base.classify_local_image(file_path) + elif isdir(file_path): + best_guess = image_base.classify_folder_images( + file_path, return_dict=True) + else: + raise Exception("Error: %s is not a valid image path!" % url) + print("\n*** Best match classification: ***") + print(best_guess) + print("") + return content_type = web_core.get_content_type(url) if content_type == 'other': raise Exception( "Error: %s does not evaluate to %s" % (url, expected_arg)) elif content_type == 'image': - best_guess = image_base.get_image_classification(url) + best_guess = image_base.classify_image_url(url) print("\n*** Best match classification: ***") print(best_guess) print("") @@ -107,14 +119,14 @@ def main(): "Best match classifications for page images:" " ***") images_classified += 1 - print best_guess + print(best_guess) if images_classified >= settings.MAX_IMAGES_PER_PAGE: break if images_classified >= settings.MAX_IMAGES_PER_PAGE: - print("\n(NOTE: Exceeded page classification limit " - "of %d images per URL! Stopping early.)" % ( - settings.MAX_IMAGES_PER_PAGE)) + print("\n(NOTE: Exceeded page classification limit " + "of %d images per URL! Stopping early.)" % ( + settings.MAX_IMAGES_PER_PAGE)) if images_classified == 0: print("\nCould not find images to classify on the page! " diff --git a/tensorpy/classify_image.py b/tensorpy/classify_image.py index 40a05a8..2c20196 100644 --- a/tensorpy/classify_image.py +++ b/tensorpy/classify_image.py @@ -13,7 +13,7 @@ os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' FLAGS = tf.app.flags.FLAGS -tf.logging.set_verbosity(tf.logging.ERROR) +tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR) tf.app.flags.DEFINE_string( 'model_dir', settings.IMAGENET_FOLDER, """Path to classify_image_graph_def.pb, """ @@ -126,4 +126,4 @@ def external_run(image): if __name__ == '__main__': - tf.app.run() + tf.compat.v1.app.run() diff --git a/tensorpy/download_imagenet.py b/tensorpy/download_imagenet.py index ab425e7..1dd49b3 100644 --- a/tensorpy/download_imagenet.py +++ b/tensorpy/download_imagenet.py @@ -26,6 +26,7 @@ def maybe_download_and_extract(): os.makedirs(dest_directory) filename = DATA_URL.split('/')[-1] filepath = os.path.join(dest_directory, filename) + print("ImageNet Inception DB filepath: %s" % filepath) if not os.path.exists(filepath): def _progress(count, block_size, total_size): sys.stdout.write('\r>> Downloading %s %.1f%%' % ( @@ -43,4 +44,4 @@ def main(_): maybe_download_and_extract() -tf.app.run() +tf.compat.v1.app.run() diff --git a/tensorpy/image_base.py b/tensorpy/image_base.py index 2b6905b..98aa7c1 100644 --- a/tensorpy/image_base.py +++ b/tensorpy/image_base.py @@ -1,18 +1,22 @@ import os import requests +import shutil +import sys import uuid -from BeautifulSoup import BeautifulSoup +from bs4 import BeautifulSoup +from io import StringIO +from os import listdir +from os.path import isdir, isfile, join from PIL import Image -from StringIO import StringIO from tensorpy import classify_image from tensorpy import settings from tensorpy import web_core def get_image_file_dimensions(file_name): - image = Image.open(file_name) - image_dimensions = image.size # (width, height) tuple - return image_dimensions + with Image.open(file_name) as image: + image_dimensions = image.size # (width, height) tuple + return image_dimensions def convert_image_file_to_jpg(file_name): @@ -22,15 +26,17 @@ def convert_image_file_to_jpg(file_name): outfile = f + ".jpg" if infile != outfile: try: - Image.open(infile).convert('RGBA').save(outfile, "JPEG") + with Image.open(infile) as image: + image.convert('RGB').save(outfile, "JPEG") except IOError: raise Exception("Cannot convert %s to jpg!" % file_name) def load_image_from_url(image_url): response = requests.get(image_url) - image = Image.open(StringIO(response.content)).convert('RGBA') - return image + with Image.open(StringIO(response.content)) as image: + image.convert('RGB') + return image def get_image_dimensions(image): @@ -57,10 +63,12 @@ def get_all_images_on_page(page_url): full_base_url = prefix + "://" + base_url + "/" html = requests.get(page_url) completed_source = web_core.rebuild_source(html.text, full_base_url) - soup = BeautifulSoup(completed_source) - imgs = soup.fetch('img', src=True, onload=None) + soup = BeautifulSoup(completed_source, "html.parser") + imgs = soup.find_all("img") image_url_list = [] for img in imgs: + if not img.has_attr("src") or img.has_attr("onload"): + continue link = img["src"].split("src=")[-1] compact_link = link.split('?')[0] if (compact_link.endswith('.png') or compact_link.endswith('.jpg') or @@ -75,26 +83,80 @@ def get_all_images_on_page(page_url): return image_url_list -def get_image_classification(image_url): +def classify_image_url(image_url): + """ Classify an image from a URL. """ downloads_folder = settings.DOWNLOADS_FOLDER - hex_name = 'temp_image_%s' % uuid.uuid4().get_hex() + hex_name = 'temp_image_%s' % uuid.uuid4().hex hex_name_png = hex_name + '.png' hex_name_jpg = hex_name + '.jpg' - web_core.save_file_as(image_url, hex_name_png) convert_image_file_to_jpg( "%s/%s" % (downloads_folder, hex_name_png)) os.rename(downloads_folder + "/" + hex_name_png, downloads_folder + "/temp_image_png.png") - best_guess = classify_image.external_run( "%s/%s" % (downloads_folder, hex_name_jpg)) os.rename(downloads_folder + "/" + hex_name_jpg, downloads_folder + "/temp_image_jpg.jpg") - return best_guess.strip() -def classify(image_url): - """ A shorter method name for get_image_classification() """ - return get_image_classification(image_url) +def get_image_classification(image_url): + # Keep original method name for backwards-compatibility + return classify_image_url(image_url) + + +def classify_local_image(file_path): + """ Classify an image from a local file path. """ + if not file_path.endswith('.jpg') and not file_path.endswith('.png'): + raise Exception("Expecting a .jpg or .png file!") + downloads_folder = settings.DOWNLOADS_FOLDER + hex_name = 'temp_image_%s' % uuid.uuid4().hex + hex_name_png = hex_name + '.png' + hex_name_jpg = hex_name + '.jpg' + shutil.copy2(file_path, os.path.join(downloads_folder, hex_name_png)) + convert_image_file_to_jpg( + "%s/%s" % (downloads_folder, hex_name_png)) + os.rename(downloads_folder + "/" + hex_name_png, + downloads_folder + "/temp_image_png.png") + best_guess = classify_image.external_run( + "%s/%s" % (downloads_folder, hex_name_jpg)) + os.rename(downloads_folder + "/" + hex_name_jpg, + downloads_folder + "/temp_image_jpg.jpg") + return best_guess + + +def classify_folder_images(folder_path, return_dict=False): + """ Classify all images from a local folder. """ + classified_images_list = [] + classified_images_dict = {} + files = [f for f in listdir(folder_path) if isfile(join(folder_path, f))] + images = [f for f in files if (f.endswith('.jpg') or f.endswith('.png'))] + total = len(images) + counter = 0 + for image in images: + counter += 1 + sys.stdout.write("\rClassifying Image %d of %s..." % (counter, total)) + sys.stdout.flush() + result = classify_local_image(os.path.join(folder_path, image)) + classified_images_list.append(result) + classified_images_dict[image] = result + sys.stdout.write("\rAll classifications have been completed!\n") + if return_dict: + return classified_images_dict + return classified_images_list + + +def classify(image_url_or_path): + """ Classify an image from a URL or local file path. + If a local folder is provided, all images in the folder + will be classified. """ + is_valid_url = web_core.is_valid_url(image_url_or_path) + if is_valid_url: + return classify_image_url(image_url_or_path) + elif isfile(image_url_or_path): + return classify_local_image(image_url_or_path) + elif isdir(image_url_or_path): + return classify_folder_images(image_url_or_path, return_dict=True) + else: + raise Exception("Expecting an image URL, file path, or folder path!") diff --git a/third_party/docker/docker_install.sh b/third_party/docker/docker_install.sh deleted file mode 100755 index 8edc900..0000000 --- a/third_party/docker/docker_install.sh +++ /dev/null @@ -1,12 +0,0 @@ -# Installs TensorPy, TensorFlow, ImageNet, and required dependancies -# (Special Docker edition!) - -pip install --upgrade pip -echo "Installing TensorPy..." -python setup.py install -echo "Initializing TensorFlow setup on an Ubuntu Docker machine..." -export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.10.0-cp27-none-linux_x86_64.whl -pip install $TF_BINARY_URL -echo "Downloading ImageNet (image database for classifying images)..." -python tensorpy/download_imagenet.py -exit