Repository files navigation

ImageSearcher

Leveraging CLIP to perform image search on personal pictures

This repository implements an Image Search engine on local photos powered by the CLIP model. It is surprisingly precise and is able to find images given complex queries. For more information, refer to the Medium blogpost here.

The added functionality of classifying pictures depending on the persons portrayed is implemented with the face_recognition library. Several filters are also available, enabling you to find your group pictures, screenshots, etc...

Setup

In a Python 3.8+ virtual environment, install either from PIP or from source:

Installation from the PIP package:

pip install image-searcher
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Installation from source

pip install -r dev_requirements.txt
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Troubleshooting: If problems are encountered building wheels for dlib during the face_recognition installation, make sure to install the python3.8-dev package (respectively python3.x-dev) and recreate the virtual environment from scratch with the aforementionned command once it is installed.

Usage

Currently, the usage is as follows. The library first computes the embeddings of all images one by one, and stores them in a picked dictionary for further reference. To compute and store information about the persons in the picture, enable the include_faces flag (note that it makes the indexing process up to 10x slower).

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)

Once this process has been done once, through Python, the library is used as such:

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)
# Option 1: Pythonic APIfromPILimportImageranked_images=searcher.rank_images("A photo of a bird.", n=5)
forimageinranked_images:
Image.open(image.image_path).convert('RGB').show()
# Option 2: Launch Flask api from codefromimage_searcher.apiimportrunrun(searcher=searcher)

Using tags in the query

Adding tags at the end of the query (example: A bird singing #photo) will filter the search based on the tag list. Supported tags for the moment are:

  • #{category}: Amongst "screenshot", "drawing", "photo", "schema", "selfie"
  • #groups: Group pictures (more than 5 people)

To come is support for:

  • #dates: Filtering based on the time period

Running the local web interface and API

After having indexed the images of interest, the Flask application loads models once and serves both the API and a browser interface for navigating results.

Specify a Config YAML file:

image_dir_path: /home/manu/Downloads/facebook_logs/messages/inbox/save_path: /home/manu/traverse: trueinclude_faces: truereindex: falsen: 42port:
host:
debug:
threaded:

Start a server:

fromimage_searcher.apiimportrun# Option 1: Through a config filerun(config_path="path_to_config_file.yml")
# Option 2: Through an instanciated Search objectfromimage_searcherimportSearchrun(searcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False))

For local browser use, launch the application with Gunicorn from the repository root:

./venv/bin/gunicorn "api.run_flask_gunicorn:create_app('config.yaml')" \
--name image_searcher \
--bind 127.0.0.1:${GUNICORN_PORT:-5000} \
--worker-tmp-dir /dev/shm \
--workers=${GUNICORN_WORKERS:-2} \
--threads=${GUNICORN_THREADS:-4} \
--worker-class=gthread \
--log-level=info \
--log-file '-' \
--timeout 30

Note: Adapt the timeout parameter (in seconds) if a lot of new images are being indexed/

Use the web interface

Open http://127.0.0.1:5000/ in a browser after starting the server.

The web interface supports:

  • Semantic text search and supported tag shortcuts such as #photo, #selfie, and #group.
  • A detail view for each result, including dimensions, file size, and the best available date metadata.
  • Face-similarity navigation when include_faces: true.
  • Related photos from the same Messenger conversation when the collection follows an inbox/<conversation>/photos/ export layout.
  • Related photos captured or shared at nearby timestamps.
  • Picture-by-picture history navigation with a back button while following related images.

For timestamp metadata, Messenger exports are read from adjacent message_*.json files when available. For regular image folders, EXIF capture dates are used when present; otherwise the filesystem modification date is displayed as a fallback.

Query the API

  • Through the search endpoint: http://127.0.0.1:5000/get_best_images?q=a+photo+of+a+bird

  • For contextual metadata and related photos: http://127.0.0.1:5000/image_context?path=/absolute/path/to/an/image.jpg

  • In Python:

importrequestsimportjsonimporturllib.parsequery="a photo of a bird"r=requests.get(f"http://127.0.0.1:5000/get_best_images?q={urllib.parse.quote(query)}")
print(json.loads(r.content)["results"])

Tips

Using this tool with vacation photos, or Messenger and Whatsapp photo archives leads to rediscovering old photos and is amazing at locating long lost ones.

Tests

Run the tests with

python -m unittest

The local web interface route tests can be run with:

python -m unittest tests.test_api.test_web_interface

and lint with:

pylint image_searcher

Contributing

This repo is a work in progress that has recently been started. As is, it computes about 10 images per second during the initial indexing phase, then is almost instantaneous during the querying phase.

Feature requests and contributions are welcomed. Improvements to the Search Web interface would also be greatly appreciated !

Todo list

Simplify and robustify the Search class instanciation:

  • Check indexation arguments are compatible with pre-loaded file
  • Store indexation arguments in pre-loaded file and give option to index new pictures with these options
  • Add the option to index for faces on previously CLIP indexed images

Speed:

  • Parallel indexation / dynamic batching based on image size
  • Data loader before indexation
  • Optimized vector computation with optimized engine (FAISS)

Features:

  • Image auto-tagging (screenshot, drawing, photo, nature, group picture, selfie, etc)
  • Image deduplication (perceptual hashing)

Embedding files:

  • Integrate with local version control (git-lfs ?)

Frontend:

  • Enable Image upload

Deployment:

  • Dockerize and orchestrate containers (image uploader, storage, indexation pipeline, inference)

About

This repository aims to implement an Image Search engine powered by the CLIP model.

Topics

Resources

Stars

48 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

ImageSearcher

Leveraging CLIP to perform image search on personal pictures

This repository implements an Image Search engine on local photos powered by the CLIP model. It is surprisingly precise and is able to find images given complex queries. For more information, refer to the Medium blogpost here.

The added functionality of classifying pictures depending on the persons portrayed is implemented with the face_recognition library. Several filters are also available, enabling you to find your group pictures, screenshots, etc...

Setup

In a Python 3.8+ virtual environment, install either from PIP or from source:

Installation from the PIP package:

pip install image-searcher
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Installation from source

pip install -r dev_requirements.txt
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Troubleshooting: If problems are encountered building wheels for dlib during the face_recognition installation, make sure to install the python3.8-dev package (respectively python3.x-dev) and recreate the virtual environment from scratch with the aforementionned command once it is installed.

Usage

Currently, the usage is as follows. The library first computes the embeddings of all images one by one, and stores them in a picked dictionary for further reference. To compute and store information about the persons in the picture, enable the include_faces flag (note that it makes the indexing process up to 10x slower).

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)

Once this process has been done once, through Python, the library is used as such:

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)
# Option 1: Pythonic APIfromPILimportImageranked_images=searcher.rank_images("A photo of a bird.", n=5)
forimageinranked_images:
Image.open(image.image_path).convert('RGB').show()
# Option 2: Launch Flask api from codefromimage_searcher.apiimportrunrun(searcher=searcher)

Using tags in the query

Adding tags at the end of the query (example: A bird singing #photo) will filter the search based on the tag list. Supported tags for the moment are:

  • #{category}: Amongst "screenshot", "drawing", "photo", "schema", "selfie"
  • #groups: Group pictures (more than 5 people)

To come is support for:

  • #dates: Filtering based on the time period

Running the local web interface and API

After having indexed the images of interest, the Flask application loads models once and serves both the API and a browser interface for navigating results.

Specify a Config YAML file:

image_dir_path: /home/manu/Downloads/facebook_logs/messages/inbox/save_path: /home/manu/traverse: trueinclude_faces: truereindex: falsen: 42port:
host:
debug:
threaded:

Start a server:

fromimage_searcher.apiimportrun# Option 1: Through a config filerun(config_path="path_to_config_file.yml")
# Option 2: Through an instanciated Search objectfromimage_searcherimportSearchrun(searcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False))

For local browser use, launch the application with Gunicorn from the repository root:

./venv/bin/gunicorn "api.run_flask_gunicorn:create_app('config.yaml')" \
--name image_searcher \
--bind 127.0.0.1:${GUNICORN_PORT:-5000} \
--worker-tmp-dir /dev/shm \
--workers=${GUNICORN_WORKERS:-2} \
--threads=${GUNICORN_THREADS:-4} \
--worker-class=gthread \
--log-level=info \
--log-file '-' \
--timeout 30

Note: Adapt the timeout parameter (in seconds) if a lot of new images are being indexed/

Use the web interface

Open http://127.0.0.1:5000/ in a browser after starting the server.

The web interface supports:

  • Semantic text search and supported tag shortcuts such as #photo, #selfie, and #group.
  • A detail view for each result, including dimensions, file size, and the best available date metadata.
  • Face-similarity navigation when include_faces: true.
  • Related photos from the same Messenger conversation when the collection follows an inbox/<conversation>/photos/ export layout.
  • Related photos captured or shared at nearby timestamps.
  • Picture-by-picture history navigation with a back button while following related images.

For timestamp metadata, Messenger exports are read from adjacent message_*.json files when available. For regular image folders, EXIF capture dates are used when present; otherwise the filesystem modification date is displayed as a fallback.

Query the API

  • Through the search endpoint: http://127.0.0.1:5000/get_best_images?q=a+photo+of+a+bird

  • For contextual metadata and related photos: http://127.0.0.1:5000/image_context?path=/absolute/path/to/an/image.jpg

  • In Python:

importrequestsimportjsonimporturllib.parsequery="a photo of a bird"r=requests.get(f"http://127.0.0.1:5000/get_best_images?q={urllib.parse.quote(query)}")
print(json.loads(r.content)["results"])

Tips

Using this tool with vacation photos, or Messenger and Whatsapp photo archives leads to rediscovering old photos and is amazing at locating long lost ones.

Tests

Run the tests with

python -m unittest

The local web interface route tests can be run with:

python -m unittest tests.test_api.test_web_interface

and lint with:

pylint image_searcher

Contributing

This repo is a work in progress that has recently been started. As is, it computes about 10 images per second during the initial indexing phase, then is almost instantaneous during the querying phase.

Feature requests and contributions are welcomed. Improvements to the Search Web interface would also be greatly appreciated !

Todo list

Simplify and robustify the Search class instanciation:

  • Check indexation arguments are compatible with pre-loaded file
  • Store indexation arguments in pre-loaded file and give option to index new pictures with these options
  • Add the option to index for faces on previously CLIP indexed images

Speed:

  • Parallel indexation / dynamic batching based on image size
  • Data loader before indexation
  • Optimized vector computation with optimized engine (FAISS)

Features:

  • Image auto-tagging (screenshot, drawing, photo, nature, group picture, selfie, etc)
  • Image deduplication (perceptual hashing)

Embedding files:

  • Integrate with local version control (git-lfs ?)

Frontend:

  • Enable Image upload

Deployment:

  • Dockerize and orchestrate containers (image uploader, storage, indexation pipeline, inference)

About

This repository aims to implement an Image Search engine powered by the CLIP model.

Topics

Resources

Stars

48 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ImageSearcher

Leveraging CLIP to perform image search on personal pictures

This repository implements an Image Search engine on local photos powered by the CLIP model. It is surprisingly precise and is able to find images given complex queries. For more information, refer to the Medium blogpost here.

The added functionality of classifying pictures depending on the persons portrayed is implemented with the face_recognition library. Several filters are also available, enabling you to find your group pictures, screenshots, etc...

Setup

In a Python 3.8+ virtual environment, install either from PIP or from source:

Installation from the PIP package:

pip install image-searcher
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Installation from source

pip install -r dev_requirements.txt
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Troubleshooting: If problems are encountered building wheels for dlib during the face_recognition installation, make sure to install the python3.8-dev package (respectively python3.x-dev) and recreate the virtual environment from scratch with the aforementionned command once it is installed.

Usage

Currently, the usage is as follows. The library first computes the embeddings of all images one by one, and stores them in a picked dictionary for further reference. To compute and store information about the persons in the picture, enable the include_faces flag (note that it makes the indexing process up to 10x slower).

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)

Once this process has been done once, through Python, the library is used as such:

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)
# Option 1: Pythonic APIfromPILimportImageranked_images=searcher.rank_images("A photo of a bird.", n=5)
forimageinranked_images:
Image.open(image.image_path).convert('RGB').show()
# Option 2: Launch Flask api from codefromimage_searcher.apiimportrunrun(searcher=searcher)

Using tags in the query

Adding tags at the end of the query (example: A bird singing #photo) will filter the search based on the tag list. Supported tags for the moment are:

  • #{category}: Amongst "screenshot", "drawing", "photo", "schema", "selfie"
  • #groups: Group pictures (more than 5 people)

To come is support for:

  • #dates: Filtering based on the time period

Running the local web interface and API

After having indexed the images of interest, the Flask application loads models once and serves both the API and a browser interface for navigating results.

Specify a Config YAML file:

image_dir_path: /home/manu/Downloads/facebook_logs/messages/inbox/save_path: /home/manu/traverse: trueinclude_faces: truereindex: falsen: 42port:
host:
debug:
threaded:

Start a server:

fromimage_searcher.apiimportrun# Option 1: Through a config filerun(config_path="path_to_config_file.yml")
# Option 2: Through an instanciated Search objectfromimage_searcherimportSearchrun(searcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False))

For local browser use, launch the application with Gunicorn from the repository root:

./venv/bin/gunicorn "api.run_flask_gunicorn:create_app('config.yaml')" \
--name image_searcher \
--bind 127.0.0.1:${GUNICORN_PORT:-5000} \
--worker-tmp-dir /dev/shm \
--workers=${GUNICORN_WORKERS:-2} \
--threads=${GUNICORN_THREADS:-4} \
--worker-class=gthread \
--log-level=info \
--log-file '-' \
--timeout 30

Note: Adapt the timeout parameter (in seconds) if a lot of new images are being indexed/

Use the web interface

Open http://127.0.0.1:5000/ in a browser after starting the server.

The web interface supports:

  • Semantic text search and supported tag shortcuts such as #photo, #selfie, and #group.
  • A detail view for each result, including dimensions, file size, and the best available date metadata.
  • Face-similarity navigation when include_faces: true.
  • Related photos from the same Messenger conversation when the collection follows an inbox/<conversation>/photos/ export layout.
  • Related photos captured or shared at nearby timestamps.
  • Picture-by-picture history navigation with a back button while following related images.

For timestamp metadata, Messenger exports are read from adjacent message_*.json files when available. For regular image folders, EXIF capture dates are used when present; otherwise the filesystem modification date is displayed as a fallback.

Query the API

  • Through the search endpoint: http://127.0.0.1:5000/get_best_images?q=a+photo+of+a+bird

  • For contextual metadata and related photos: http://127.0.0.1:5000/image_context?path=/absolute/path/to/an/image.jpg

  • In Python:

importrequestsimportjsonimporturllib.parsequery="a photo of a bird"r=requests.get(f"http://127.0.0.1:5000/get_best_images?q={urllib.parse.quote(query)}")
print(json.loads(r.content)["results"])

Tips

Using this tool with vacation photos, or Messenger and Whatsapp photo archives leads to rediscovering old photos and is amazing at locating long lost ones.

Tests

Run the tests with

python -m unittest

The local web interface route tests can be run with:

python -m unittest tests.test_api.test_web_interface

and lint with:

pylint image_searcher

Contributing

This repo is a work in progress that has recently been started. As is, it computes about 10 images per second during the initial indexing phase, then is almost instantaneous during the querying phase.

Feature requests and contributions are welcomed. Improvements to the Search Web interface would also be greatly appreciated !

Todo list

Simplify and robustify the Search class instanciation:

  • Check indexation arguments are compatible with pre-loaded file
  • Store indexation arguments in pre-loaded file and give option to index new pictures with these options
  • Add the option to index for faces on previously CLIP indexed images

Speed:

  • Parallel indexation / dynamic batching based on image size
  • Data loader before indexation
  • Optimized vector computation with optimized engine (FAISS)

Features:

  • Image auto-tagging (screenshot, drawing, photo, nature, group picture, selfie, etc)
  • Image deduplication (perceptual hashing)

Embedding files:

  • Integrate with local version control (git-lfs ?)

Frontend:

  • Enable Image upload

Deployment:

  • Dockerize and orchestrate containers (image uploader, storage, indexation pipeline, inference)

About

This repository aims to implement an Image Search engine powered by the CLIP model.

Topics

Resources

Stars

48 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ImageSearcher

Leveraging CLIP to perform image search on personal pictures

This repository implements an Image Search engine on local photos powered by the CLIP model. It is surprisingly precise and is able to find images given complex queries. For more information, refer to the Medium blogpost here.

The added functionality of classifying pictures depending on the persons portrayed is implemented with the face_recognition library. Several filters are also available, enabling you to find your group pictures, screenshots, etc...

Setup

In a Python 3.8+ virtual environment, install either from PIP or from source:

Installation from the PIP package:

pip install image-searcher
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Installation from source

pip install -r dev_requirements.txt
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Troubleshooting: If problems are encountered building wheels for dlib during the face_recognition installation, make sure to install the python3.8-dev package (respectively python3.x-dev) and recreate the virtual environment from scratch with the aforementionned command once it is installed.

Usage

Currently, the usage is as follows. The library first computes the embeddings of all images one by one, and stores them in a picked dictionary for further reference. To compute and store information about the persons in the picture, enable the include_faces flag (note that it makes the indexing process up to 10x slower).

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)

Once this process has been done once, through Python, the library is used as such:

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)
# Option 1: Pythonic APIfromPILimportImageranked_images=searcher.rank_images("A photo of a bird.", n=5)
forimageinranked_images:
Image.open(image.image_path).convert('RGB').show()
# Option 2: Launch Flask api from codefromimage_searcher.apiimportrunrun(searcher=searcher)

Using tags in the query

Adding tags at the end of the query (example: A bird singing #photo) will filter the search based on the tag list. Supported tags for the moment are:

  • #{category}: Amongst "screenshot", "drawing", "photo", "schema", "selfie"
  • #groups: Group pictures (more than 5 people)

To come is support for:

  • #dates: Filtering based on the time period

Running the local web interface and API

After having indexed the images of interest, the Flask application loads models once and serves both the API and a browser interface for navigating results.

Specify a Config YAML file:

image_dir_path: /home/manu/Downloads/facebook_logs/messages/inbox/save_path: /home/manu/traverse: trueinclude_faces: truereindex: falsen: 42port:
host:
debug:
threaded:

Start a server:

fromimage_searcher.apiimportrun# Option 1: Through a config filerun(config_path="path_to_config_file.yml")
# Option 2: Through an instanciated Search objectfromimage_searcherimportSearchrun(searcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False))

For local browser use, launch the application with Gunicorn from the repository root:

./venv/bin/gunicorn "api.run_flask_gunicorn:create_app('config.yaml')" \
--name image_searcher \
--bind 127.0.0.1:${GUNICORN_PORT:-5000} \
--worker-tmp-dir /dev/shm \
--workers=${GUNICORN_WORKERS:-2} \
--threads=${GUNICORN_THREADS:-4} \
--worker-class=gthread \
--log-level=info \
--log-file '-' \
--timeout 30

Note: Adapt the timeout parameter (in seconds) if a lot of new images are being indexed/

Use the web interface

Open http://127.0.0.1:5000/ in a browser after starting the server.

The web interface supports:

  • Semantic text search and supported tag shortcuts such as #photo, #selfie, and #group.
  • A detail view for each result, including dimensions, file size, and the best available date metadata.
  • Face-similarity navigation when include_faces: true.
  • Related photos from the same Messenger conversation when the collection follows an inbox/<conversation>/photos/ export layout.
  • Related photos captured or shared at nearby timestamps.
  • Picture-by-picture history navigation with a back button while following related images.

For timestamp metadata, Messenger exports are read from adjacent message_*.json files when available. For regular image folders, EXIF capture dates are used when present; otherwise the filesystem modification date is displayed as a fallback.

Query the API

  • Through the search endpoint: http://127.0.0.1:5000/get_best_images?q=a+photo+of+a+bird

  • For contextual metadata and related photos: http://127.0.0.1:5000/image_context?path=/absolute/path/to/an/image.jpg

  • In Python:

importrequestsimportjsonimporturllib.parsequery="a photo of a bird"r=requests.get(f"http://127.0.0.1:5000/get_best_images?q={urllib.parse.quote(query)}")
print(json.loads(r.content)["results"])

Tips

Using this tool with vacation photos, or Messenger and Whatsapp photo archives leads to rediscovering old photos and is amazing at locating long lost ones.

Tests

Run the tests with

python -m unittest

The local web interface route tests can be run with:

python -m unittest tests.test_api.test_web_interface

and lint with:

pylint image_searcher

Contributing

This repo is a work in progress that has recently been started. As is, it computes about 10 images per second during the initial indexing phase, then is almost instantaneous during the querying phase.

Feature requests and contributions are welcomed. Improvements to the Search Web interface would also be greatly appreciated !

Todo list

Simplify and robustify the Search class instanciation:

  • Check indexation arguments are compatible with pre-loaded file
  • Store indexation arguments in pre-loaded file and give option to index new pictures with these options
  • Add the option to index for faces on previously CLIP indexed images

Speed:

  • Parallel indexation / dynamic batching based on image size
  • Data loader before indexation
  • Optimized vector computation with optimized engine (FAISS)

Features:

  • Image auto-tagging (screenshot, drawing, photo, nature, group picture, selfie, etc)
  • Image deduplication (perceptual hashing)

Embedding files:

  • Integrate with local version control (git-lfs ?)

Frontend:

  • Enable Image upload

Deployment:

  • Dockerize and orchestrate containers (image uploader, storage, indexation pipeline, inference)

About

This repository aims to implement an Image Search engine powered by the CLIP model.

Topics

Resources

Stars

48 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

ImageSearcher

Leveraging CLIP to perform image search on personal pictures

This repository implements an Image Search engine on local photos powered by the CLIP model. It is surprisingly precise and is able to find images given complex queries. For more information, refer to the Medium blogpost here.

The added functionality of classifying pictures depending on the persons portrayed is implemented with the face_recognition library. Several filters are also available, enabling you to find your group pictures, screenshots, etc...

Setup

In a Python 3.8+ virtual environment, install either from PIP or from source:

Installation from the PIP package:

pip install image-searcher
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Installation from source

pip install -r dev_requirements.txt
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Troubleshooting: If problems are encountered building wheels for dlib during the face_recognition installation, make sure to install the python3.8-dev package (respectively python3.x-dev) and recreate the virtual environment from scratch with the aforementionned command once it is installed.

Usage

Currently, the usage is as follows. The library first computes the embeddings of all images one by one, and stores them in a picked dictionary for further reference. To compute and store information about the persons in the picture, enable the include_faces flag (note that it makes the indexing process up to 10x slower).

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)

Once this process has been done once, through Python, the library is used as such:

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)
# Option 1: Pythonic APIfromPILimportImageranked_images=searcher.rank_images("A photo of a bird.", n=5)
forimageinranked_images:
Image.open(image.image_path).convert('RGB').show()
# Option 2: Launch Flask api from codefromimage_searcher.apiimportrunrun(searcher=searcher)

Using tags in the query

Adding tags at the end of the query (example: A bird singing #photo) will filter the search based on the tag list. Supported tags for the moment are:

  • #{category}: Amongst "screenshot", "drawing", "photo", "schema", "selfie"
  • #groups: Group pictures (more than 5 people)

To come is support for:

  • #dates: Filtering based on the time period

Running the local web interface and API

After having indexed the images of interest, the Flask application loads models once and serves both the API and a browser interface for navigating results.

Specify a Config YAML file:

image_dir_path: /home/manu/Downloads/facebook_logs/messages/inbox/save_path: /home/manu/traverse: trueinclude_faces: truereindex: falsen: 42port:
host:
debug:
threaded:

Start a server:

fromimage_searcher.apiimportrun# Option 1: Through a config filerun(config_path="path_to_config_file.yml")
# Option 2: Through an instanciated Search objectfromimage_searcherimportSearchrun(searcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False))

For local browser use, launch the application with Gunicorn from the repository root:

./venv/bin/gunicorn "api.run_flask_gunicorn:create_app('config.yaml')" \
--name image_searcher \
--bind 127.0.0.1:${GUNICORN_PORT:-5000} \
--worker-tmp-dir /dev/shm \
--workers=${GUNICORN_WORKERS:-2} \
--threads=${GUNICORN_THREADS:-4} \
--worker-class=gthread \
--log-level=info \
--log-file '-' \
--timeout 30

Note: Adapt the timeout parameter (in seconds) if a lot of new images are being indexed/

Use the web interface

Open http://127.0.0.1:5000/ in a browser after starting the server.

The web interface supports:

  • Semantic text search and supported tag shortcuts such as #photo, #selfie, and #group.
  • A detail view for each result, including dimensions, file size, and the best available date metadata.
  • Face-similarity navigation when include_faces: true.
  • Related photos from the same Messenger conversation when the collection follows an inbox/<conversation>/photos/ export layout.
  • Related photos captured or shared at nearby timestamps.
  • Picture-by-picture history navigation with a back button while following related images.

For timestamp metadata, Messenger exports are read from adjacent message_*.json files when available. For regular image folders, EXIF capture dates are used when present; otherwise the filesystem modification date is displayed as a fallback.

Query the API

  • Through the search endpoint: http://127.0.0.1:5000/get_best_images?q=a+photo+of+a+bird

  • For contextual metadata and related photos: http://127.0.0.1:5000/image_context?path=/absolute/path/to/an/image.jpg

  • In Python:

importrequestsimportjsonimporturllib.parsequery="a photo of a bird"r=requests.get(f"http://127.0.0.1:5000/get_best_images?q={urllib.parse.quote(query)}")
print(json.loads(r.content)["results"])

Tips

Using this tool with vacation photos, or Messenger and Whatsapp photo archives leads to rediscovering old photos and is amazing at locating long lost ones.

Tests

Run the tests with

python -m unittest

The local web interface route tests can be run with:

python -m unittest tests.test_api.test_web_interface

and lint with:

pylint image_searcher

Contributing

This repo is a work in progress that has recently been started. As is, it computes about 10 images per second during the initial indexing phase, then is almost instantaneous during the querying phase.

Feature requests and contributions are welcomed. Improvements to the Search Web interface would also be greatly appreciated !

Todo list

Simplify and robustify the Search class instanciation:

  • Check indexation arguments are compatible with pre-loaded file
  • Store indexation arguments in pre-loaded file and give option to index new pictures with these options
  • Add the option to index for faces on previously CLIP indexed images

Speed:

  • Parallel indexation / dynamic batching based on image size
  • Data loader before indexation
  • Optimized vector computation with optimized engine (FAISS)

Features:

  • Image auto-tagging (screenshot, drawing, photo, nature, group picture, selfie, etc)
  • Image deduplication (perceptual hashing)

Embedding files:

  • Integrate with local version control (git-lfs ?)

Frontend:

  • Enable Image upload

Deployment:

  • Dockerize and orchestrate containers (image uploader, storage, indexation pipeline, inference)

About

This repository aims to implement an Image Search engine powered by the CLIP model.

Topics

Resources

Stars

48 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ImageSearcher

Leveraging CLIP to perform image search on personal pictures

This repository implements an Image Search engine on local photos powered by the CLIP model. It is surprisingly precise and is able to find images given complex queries. For more information, refer to the Medium blogpost here.

The added functionality of classifying pictures depending on the persons portrayed is implemented with the face_recognition library. Several filters are also available, enabling you to find your group pictures, screenshots, etc...

Setup

In a Python 3.8+ virtual environment, install either from PIP or from source:

Installation from the PIP package:

pip install image-searcher
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Installation from source

pip install -r dev_requirements.txt
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Troubleshooting: If problems are encountered building wheels for dlib during the face_recognition installation, make sure to install the python3.8-dev package (respectively python3.x-dev) and recreate the virtual environment from scratch with the aforementionned command once it is installed.

Usage

Currently, the usage is as follows. The library first computes the embeddings of all images one by one, and stores them in a picked dictionary for further reference. To compute and store information about the persons in the picture, enable the include_faces flag (note that it makes the indexing process up to 10x slower).

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)

Once this process has been done once, through Python, the library is used as such:

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)
# Option 1: Pythonic APIfromPILimportImageranked_images=searcher.rank_images("A photo of a bird.", n=5)
forimageinranked_images:
Image.open(image.image_path).convert('RGB').show()
# Option 2: Launch Flask api from codefromimage_searcher.apiimportrunrun(searcher=searcher)

Using tags in the query

Adding tags at the end of the query (example: A bird singing #photo) will filter the search based on the tag list. Supported tags for the moment are:

  • #{category}: Amongst "screenshot", "drawing", "photo", "schema", "selfie"
  • #groups: Group pictures (more than 5 people)

To come is support for:

  • #dates: Filtering based on the time period

Running the local web interface and API

After having indexed the images of interest, the Flask application loads models once and serves both the API and a browser interface for navigating results.

Specify a Config YAML file:

image_dir_path: /home/manu/Downloads/facebook_logs/messages/inbox/save_path: /home/manu/traverse: trueinclude_faces: truereindex: falsen: 42port:
host:
debug:
threaded:

Start a server:

fromimage_searcher.apiimportrun# Option 1: Through a config filerun(config_path="path_to_config_file.yml")
# Option 2: Through an instanciated Search objectfromimage_searcherimportSearchrun(searcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False))

For local browser use, launch the application with Gunicorn from the repository root:

./venv/bin/gunicorn "api.run_flask_gunicorn:create_app('config.yaml')" \
--name image_searcher \
--bind 127.0.0.1:${GUNICORN_PORT:-5000} \
--worker-tmp-dir /dev/shm \
--workers=${GUNICORN_WORKERS:-2} \
--threads=${GUNICORN_THREADS:-4} \
--worker-class=gthread \
--log-level=info \
--log-file '-' \
--timeout 30

Note: Adapt the timeout parameter (in seconds) if a lot of new images are being indexed/

Use the web interface

Open http://127.0.0.1:5000/ in a browser after starting the server.

The web interface supports:

  • Semantic text search and supported tag shortcuts such as #photo, #selfie, and #group.
  • A detail view for each result, including dimensions, file size, and the best available date metadata.
  • Face-similarity navigation when include_faces: true.
  • Related photos from the same Messenger conversation when the collection follows an inbox/<conversation>/photos/ export layout.
  • Related photos captured or shared at nearby timestamps.
  • Picture-by-picture history navigation with a back button while following related images.

For timestamp metadata, Messenger exports are read from adjacent message_*.json files when available. For regular image folders, EXIF capture dates are used when present; otherwise the filesystem modification date is displayed as a fallback.

Query the API

  • Through the search endpoint: http://127.0.0.1:5000/get_best_images?q=a+photo+of+a+bird

  • For contextual metadata and related photos: http://127.0.0.1:5000/image_context?path=/absolute/path/to/an/image.jpg

  • In Python:

importrequestsimportjsonimporturllib.parsequery="a photo of a bird"r=requests.get(f"http://127.0.0.1:5000/get_best_images?q={urllib.parse.quote(query)}")
print(json.loads(r.content)["results"])

Tips

Using this tool with vacation photos, or Messenger and Whatsapp photo archives leads to rediscovering old photos and is amazing at locating long lost ones.

Tests

Run the tests with

python -m unittest

The local web interface route tests can be run with:

python -m unittest tests.test_api.test_web_interface

and lint with:

pylint image_searcher

Contributing

This repo is a work in progress that has recently been started. As is, it computes about 10 images per second during the initial indexing phase, then is almost instantaneous during the querying phase.

Feature requests and contributions are welcomed. Improvements to the Search Web interface would also be greatly appreciated !

Todo list

Simplify and robustify the Search class instanciation:

  • Check indexation arguments are compatible with pre-loaded file
  • Store indexation arguments in pre-loaded file and give option to index new pictures with these options
  • Add the option to index for faces on previously CLIP indexed images

Speed:

  • Parallel indexation / dynamic batching based on image size
  • Data loader before indexation
  • Optimized vector computation with optimized engine (FAISS)

Features:

  • Image auto-tagging (screenshot, drawing, photo, nature, group picture, selfie, etc)
  • Image deduplication (perceptual hashing)

Embedding files:

  • Integrate with local version control (git-lfs ?)

Frontend:

  • Enable Image upload

Deployment:

  • Dockerize and orchestrate containers (image uploader, storage, indexation pipeline, inference)

About

This repository aims to implement an Image Search engine powered by the CLIP model.

Topics

Resources

Stars

48 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ImageSearcher

Leveraging CLIP to perform image search on personal pictures

This repository implements an Image Search engine on local photos powered by the CLIP model. It is surprisingly precise and is able to find images given complex queries. For more information, refer to the Medium blogpost here.

The added functionality of classifying pictures depending on the persons portrayed is implemented with the face_recognition library. Several filters are also available, enabling you to find your group pictures, screenshots, etc...

Setup

In a Python 3.8+ virtual environment, install either from PIP or from source:

Installation from the PIP package:

pip install image-searcher
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Installation from source

pip install -r dev_requirements.txt
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Troubleshooting: If problems are encountered building wheels for dlib during the face_recognition installation, make sure to install the python3.8-dev package (respectively python3.x-dev) and recreate the virtual environment from scratch with the aforementionned command once it is installed.

Usage

Currently, the usage is as follows. The library first computes the embeddings of all images one by one, and stores them in a picked dictionary for further reference. To compute and store information about the persons in the picture, enable the include_faces flag (note that it makes the indexing process up to 10x slower).

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)

Once this process has been done once, through Python, the library is used as such:

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)
# Option 1: Pythonic APIfromPILimportImageranked_images=searcher.rank_images("A photo of a bird.", n=5)
forimageinranked_images:
Image.open(image.image_path).convert('RGB').show()
# Option 2: Launch Flask api from codefromimage_searcher.apiimportrunrun(searcher=searcher)

Using tags in the query

Adding tags at the end of the query (example: A bird singing #photo) will filter the search based on the tag list. Supported tags for the moment are:

  • #{category}: Amongst "screenshot", "drawing", "photo", "schema", "selfie"
  • #groups: Group pictures (more than 5 people)

To come is support for:

  • #dates: Filtering based on the time period

Running the local web interface and API

After having indexed the images of interest, the Flask application loads models once and serves both the API and a browser interface for navigating results.

Specify a Config YAML file:

image_dir_path: /home/manu/Downloads/facebook_logs/messages/inbox/save_path: /home/manu/traverse: trueinclude_faces: truereindex: falsen: 42port:
host:
debug:
threaded:

Start a server:

fromimage_searcher.apiimportrun# Option 1: Through a config filerun(config_path="path_to_config_file.yml")
# Option 2: Through an instanciated Search objectfromimage_searcherimportSearchrun(searcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False))

For local browser use, launch the application with Gunicorn from the repository root:

./venv/bin/gunicorn "api.run_flask_gunicorn:create_app('config.yaml')" \
--name image_searcher \
--bind 127.0.0.1:${GUNICORN_PORT:-5000} \
--worker-tmp-dir /dev/shm \
--workers=${GUNICORN_WORKERS:-2} \
--threads=${GUNICORN_THREADS:-4} \
--worker-class=gthread \
--log-level=info \
--log-file '-' \
--timeout 30

Note: Adapt the timeout parameter (in seconds) if a lot of new images are being indexed/

Use the web interface

Open http://127.0.0.1:5000/ in a browser after starting the server.

The web interface supports:

  • Semantic text search and supported tag shortcuts such as #photo, #selfie, and #group.
  • A detail view for each result, including dimensions, file size, and the best available date metadata.
  • Face-similarity navigation when include_faces: true.
  • Related photos from the same Messenger conversation when the collection follows an inbox/<conversation>/photos/ export layout.
  • Related photos captured or shared at nearby timestamps.
  • Picture-by-picture history navigation with a back button while following related images.

For timestamp metadata, Messenger exports are read from adjacent message_*.json files when available. For regular image folders, EXIF capture dates are used when present; otherwise the filesystem modification date is displayed as a fallback.

Query the API

  • Through the search endpoint: http://127.0.0.1:5000/get_best_images?q=a+photo+of+a+bird

  • For contextual metadata and related photos: http://127.0.0.1:5000/image_context?path=/absolute/path/to/an/image.jpg

  • In Python:

importrequestsimportjsonimporturllib.parsequery="a photo of a bird"r=requests.get(f"http://127.0.0.1:5000/get_best_images?q={urllib.parse.quote(query)}")
print(json.loads(r.content)["results"])

Tips

Using this tool with vacation photos, or Messenger and Whatsapp photo archives leads to rediscovering old photos and is amazing at locating long lost ones.

Tests

Run the tests with

python -m unittest

The local web interface route tests can be run with:

python -m unittest tests.test_api.test_web_interface

and lint with:

pylint image_searcher

Contributing

This repo is a work in progress that has recently been started. As is, it computes about 10 images per second during the initial indexing phase, then is almost instantaneous during the querying phase.

Feature requests and contributions are welcomed. Improvements to the Search Web interface would also be greatly appreciated !

Todo list

Simplify and robustify the Search class instanciation:

  • Check indexation arguments are compatible with pre-loaded file
  • Store indexation arguments in pre-loaded file and give option to index new pictures with these options
  • Add the option to index for faces on previously CLIP indexed images

Speed:

  • Parallel indexation / dynamic batching based on image size
  • Data loader before indexation
  • Optimized vector computation with optimized engine (FAISS)

Features:

  • Image auto-tagging (screenshot, drawing, photo, nature, group picture, selfie, etc)
  • Image deduplication (perceptual hashing)

Embedding files:

  • Integrate with local version control (git-lfs ?)

Frontend:

  • Enable Image upload

Deployment:

  • Dockerize and orchestrate containers (image uploader, storage, indexation pipeline, inference)

About

This repository aims to implement an Image Search engine powered by the CLIP model.

Topics

Resources

Stars

48 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

ImageSearcher

Leveraging CLIP to perform image search on personal pictures

This repository implements an Image Search engine on local photos powered by the CLIP model. It is surprisingly precise and is able to find images given complex queries. For more information, refer to the Medium blogpost here.

The added functionality of classifying pictures depending on the persons portrayed is implemented with the face_recognition library. Several filters are also available, enabling you to find your group pictures, screenshots, etc...

Setup

In a Python 3.8+ virtual environment, install either from PIP or from source:

Installation from the PIP package:

pip install image-searcher
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Installation from source

pip install -r dev_requirements.txt
pip install face_recognition # Optional to enable face features
pip install flask flask_cors # Optional to enable a flask api

Troubleshooting: If problems are encountered building wheels for dlib during the face_recognition installation, make sure to install the python3.8-dev package (respectively python3.x-dev) and recreate the virtual environment from scratch with the aforementionned command once it is installed.

Usage

Currently, the usage is as follows. The library first computes the embeddings of all images one by one, and stores them in a picked dictionary for further reference. To compute and store information about the persons in the picture, enable the include_faces flag (note that it makes the indexing process up to 10x slower).

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)

Once this process has been done once, through Python, the library is used as such:

fromimage_searcherimportSearchsearcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False)
# Option 1: Pythonic APIfromPILimportImageranked_images=searcher.rank_images("A photo of a bird.", n=5)
forimageinranked_images:
Image.open(image.image_path).convert('RGB').show()
# Option 2: Launch Flask api from codefromimage_searcher.apiimportrunrun(searcher=searcher)

Using tags in the query

Adding tags at the end of the query (example: A bird singing #photo) will filter the search based on the tag list. Supported tags for the moment are:

  • #{category}: Amongst "screenshot", "drawing", "photo", "schema", "selfie"
  • #groups: Group pictures (more than 5 people)

To come is support for:

  • #dates: Filtering based on the time period

Running the local web interface and API

After having indexed the images of interest, the Flask application loads models once and serves both the API and a browser interface for navigating results.

Specify a Config YAML file:

image_dir_path: /home/manu/Downloads/facebook_logs/messages/inbox/save_path: /home/manu/traverse: trueinclude_faces: truereindex: falsen: 42port:
host:
debug:
threaded:

Start a server:

fromimage_searcher.apiimportrun# Option 1: Through a config filerun(config_path="path_to_config_file.yml")
# Option 2: Through an instanciated Search objectfromimage_searcherimportSearchrun(searcher=Search(image_dir_path="/home/manu/perso/ImageSearcher/data/", traverse=True, include_faces=False))

For local browser use, launch the application with Gunicorn from the repository root:

./venv/bin/gunicorn "api.run_flask_gunicorn:create_app('config.yaml')" \
--name image_searcher \
--bind 127.0.0.1:${GUNICORN_PORT:-5000} \
--worker-tmp-dir /dev/shm \
--workers=${GUNICORN_WORKERS:-2} \
--threads=${GUNICORN_THREADS:-4} \
--worker-class=gthread \
--log-level=info \
--log-file '-' \
--timeout 30

Note: Adapt the timeout parameter (in seconds) if a lot of new images are being indexed/

Use the web interface

Open http://127.0.0.1:5000/ in a browser after starting the server.

The web interface supports:

  • Semantic text search and supported tag shortcuts such as #photo, #selfie, and #group.
  • A detail view for each result, including dimensions, file size, and the best available date metadata.
  • Face-similarity navigation when include_faces: true.
  • Related photos from the same Messenger conversation when the collection follows an inbox/<conversation>/photos/ export layout.
  • Related photos captured or shared at nearby timestamps.
  • Picture-by-picture history navigation with a back button while following related images.

For timestamp metadata, Messenger exports are read from adjacent message_*.json files when available. For regular image folders, EXIF capture dates are used when present; otherwise the filesystem modification date is displayed as a fallback.

Query the API

  • Through the search endpoint: http://127.0.0.1:5000/get_best_images?q=a+photo+of+a+bird

  • For contextual metadata and related photos: http://127.0.0.1:5000/image_context?path=/absolute/path/to/an/image.jpg

  • In Python:

importrequestsimportjsonimporturllib.parsequery="a photo of a bird"r=requests.get(f"http://127.0.0.1:5000/get_best_images?q={urllib.parse.quote(query)}")
print(json.loads(r.content)["results"])

Tips

Using this tool with vacation photos, or Messenger and Whatsapp photo archives leads to rediscovering old photos and is amazing at locating long lost ones.

Tests

Run the tests with

python -m unittest

The local web interface route tests can be run with:

python -m unittest tests.test_api.test_web_interface

and lint with:

pylint image_searcher

Contributing

This repo is a work in progress that has recently been started. As is, it computes about 10 images per second during the initial indexing phase, then is almost instantaneous during the querying phase.

Feature requests and contributions are welcomed. Improvements to the Search Web interface would also be greatly appreciated !

Todo list

Simplify and robustify the Search class instanciation:

  • Check indexation arguments are compatible with pre-loaded file
  • Store indexation arguments in pre-loaded file and give option to index new pictures with these options
  • Add the option to index for faces on previously CLIP indexed images

Speed:

  • Parallel indexation / dynamic batching based on image size
  • Data loader before indexation
  • Optimized vector computation with optimized engine (FAISS)

Features:

  • Image auto-tagging (screenshot, drawing, photo, nature, group picture, selfie, etc)
  • Image deduplication (perceptual hashing)

Embedding files:

  • Integrate with local version control (git-lfs ?)

Frontend:

  • Enable Image upload

Deployment:

  • Dockerize and orchestrate containers (image uploader, storage, indexation pipeline, inference)

About

This repository aims to implement an Image Search engine powered by the CLIP model.

Topics

Resources

Stars

48 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages