{"id":13544975,"url":"https://github.com/dsys/match","last_synced_at":"2025-05-16T11:04:47.212Z","repository":{"id":41351352,"uuid":"53703716","full_name":"dsys/match","owner":"dsys","description":":crystal_ball: Scalable reverse image search built on Kubernetes and Elasticsearch","archived":false,"fork":false,"pushed_at":"2020-07-25T18:01:58.000Z","size":628,"stargazers_count":1254,"open_issues_count":18,"forks_count":150,"subscribers_count":36,"default_branch":"master","last_synced_at":"2025-03-14T05:01:56.729Z","etag":null,"topics":["elasticsearch","image-signatures","kubernetes-cluster"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dsys.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-11T23:48:52.000Z","updated_at":"2025-03-14T03:10:42.000Z","dependencies_parsed_at":"2022-08-10T01:54:51.208Z","dependency_job_id":null,"html_url":"https://github.com/dsys/match","commit_stats":null,"previous_names":["usepavlov/match"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsys%2Fmatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsys%2Fmatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsys%2Fmatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dsys%2Fmatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dsys","download_url":"https://codeload.github.com/dsys/match/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254518384,"owners_count":22084374,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elasticsearch","image-signatures","kubernetes-cluster"],"created_at":"2024-08-01T11:00:55.723Z","updated_at":"2025-05-16T11:04:42.198Z","avatar_url":"https://github.com/dsys.png","language":"Python","funding_links":[],"categories":["Images","Python"],"sub_categories":["Frameworks"],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/dsys/match/master/resources/logo.png\" alt=\"logo\" width=\"220\" /\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cstrong\u003eScalable reverse image search\u003c/strong\u003e\u003cbr /\u003e\u003cem\u003ebuilt on \u003ca href=\"http://kubernetes.io/\"\u003eKubernetes\u003c/a\u003e and \u003ca href=\"https://www.elastic.co/\"\u003eElasticsearch\u003c/a\u003e\u003c/em\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"https://github.com/dsys/match/stargazers\"\u003e\u003cimg src=\"https://img.shields.io/github/stars/dsys/match.svg?style=flat\" alt=\"GitHub stars\" /\u003e\u003c/a\u003e \u003ca href=\"https://hub.docker.com/r/dsys/match/\"\u003e\u003cimg src=\"https://img.shields.io/docker/pulls/dsys/match.svg\" alt=\"Docker Pulls\" /\u003e\u003c/a\u003e \u003ca href=\"http://kubernetes.io\"\u003e\u003cimg src=\"https://img.shields.io/badge/kubernetes-ready-brightgreen.svg?style=flat\" alt=\"Kubernetes shield\" /\u003e\u003c/a\u003e \u003ca href=\"https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fdsys%2Fmatch?ref=badge_shield\" alt=\"FOSSA Status\"\u003e\u003cimg src=\"https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fdsys%2Fmatch.svg?type=shield\"/\u003e\u003c/a\u003e\u003c/p\u003e\n\n**Match** makes it easy to search for images that look similar to each other. Using a state-of-the-art perceptual hash, it is invariant to scaling and 90 degree rotations. Its HTTP API is quick to integrate and flexible for a number of reverse image search applications. Kubernetes and Elasticsearch allow Match to scale to billions of images with ease while giving you full control over where your data is stored. Match uses the awesome [ascribe/image-match](https://github.com/ascribe/image-match) under the hood for most of the image search legwork.\n\n1. [Getting Started](#getting-started)\n2. [API](#api)\n3. [Development](#development)\n4. [License and Acknowledgements](#license-and-acknowledgements)\n\n## Getting Started\n\nIf you already have ElasticSearch running:\n```\n$ docker run -e ELASTICSEARCH_URL=https://daisy.us-west-1.es.amazonaws.com -it dsys/match\n```\n\nIf you want to run ElasticSearch locally as well, have [`docker-compose`](https://docs.docker.com/compose/) installed on your system, clone this repository and type:\n```\n$ make dev\n```\n\nMatch is packaged as a Docker container ([dsys/match](https://hub.docker.com/r/dsys/match/) on Docker Hub), making it highly portable and scalable to billions of images. You can configure a few options using environment variables:\n\n* **WORKER_COUNT** *(default: `4`)*\n\n  The number of gunicorn workers to spin up.\n\n* **ELASTICSEARCH_URL** *(default: `elasticsearch:9200`)*\n\n  A URL pointing to the Elasticsearch database where image signatures are to be stored. If you don't want to host your own Elasticsearch cluster, consider using [AWS Elasticsearch Service](https://aws.amazon.com/elasticsearch-service/). That's what we use.\n\n* **ELASTICSEARCH_INDEX** *(default: images)*\n\n  The index in the Elasticsearch database where image signatures are to be stored.\n\n* **ELASTICSEARCH_DOC_TYPE** *(default: images)*\n\n  The doc type used for storing image signatures.\n\n\n### Using in your own Kubernetes cluster\n\nYou can configure the service, replication controller, and secret like so:\n\n```yaml\n# match-service.yml\napiVersion: v1\nkind: Service\nmetadata:\n  name: match\nspec:\n  ports:\n  - name: http\n    port: 80\n    protocol: TCP\n  selector:\n    app: match\n```\n\n```yaml\n# match-rc.yml\napiVersion: v1\nkind: ReplicationController\nmetadata:\n  name: match\nspec:\n  replicas: 1\n  selector:\n    app: match\n  template:\n    metadata:\n      labels:\n        app: match\n    spec:\n      containers:\n      - name: match\n        image: dsys/match:latest\n        ports:\n        - containerPort: 80\n        env:\n        - name: WORKER_COUNT\n          value: \"4\"\n        - name: ELASTICSEARCH_URL\n          valueFrom:\n            secretKeyRef:\n              name: match\n              key: elasticsearch.url\n        - name: ELASTICSEARCH_INDEX\n          valueFrom:\n            secretKeyRef:\n              name: match\n              key: elasticsearch.index\n        - name: ELASTICSEARCH_DOC_TYPE\n          valueFrom:\n            secretKeyRef:\n              name: match\n              key: elasticsearch.doc-type\n```\n\n```yaml\n# match-secret.yml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: match\ndata:\n  # https://daisy.us-west-1.es.amazonaws.com (change me)\n  elasticsearch.url: aHR0cHM6Ly9kYWlzeS51cy13ZXN0LTEuZXMuYW1hem9uYXdzLmNvbQ==\n\n  # images\n  elasticsearch.index: aW1hZ2Vz\n\n  # images\n  elasticsearch.doc-type: aW1hZ2Vz\n```\n\n## API\n\nMatch has a simple HTTP API. All request parameters are specified via `application/x-www-form-urlencoded` or `multipart/form-data`.\n\n* [POST `/add`](#post-add)\n* [DELETE `/delete`](#delete-delete)\n* [POST `/search`](#post-search)\n* [POST `/compare`](#post-compare)\n* [GET `/count`](#get-count)\n* [GET `/list`](#get-list)\n* [GET `/ping`](#get-ping)\n\n---\n\n### POST `/add`\n\nAdds an image signature to the database.\n\n#### Parameters\n\n* **url** or **image** *(required)*\n\n  The image to add to the database. It may be provided as a URL via `url` or as a `multipart/form-data` file upload via `image`.\n\n* **filepath** *(required)*\n\n  The path to save the image to in the database. If another image already exists at the given path, it will be overwritten.\n\n* **metadata** *(default: None)*\n\n  An arbitrary JSON object featuring meta data to attach to the image.\n\n#### Example Response\n\n```json\n{\n  \"status\": \"ok\",\n  \"error\": [],\n  \"method\": \"add\",\n  \"result\": []\n}\n```\n\n---\n\n### DELETE `/delete`\n\nDeletes an image signature from the database.\n\n#### Parameters\n\n* **filepath** *(required)*\n\n  The path of the image signature in the database.\n\n#### Example Response\n\n```json\n{\n  \"status\": \"ok\",\n  \"error\": [],\n  \"method\": \"delete\",\n  \"result\": []\n}\n```\n\n---\n\n### POST `/search`\n\nSearches for a similar image in the database. Scores range from 0 to 100, with 100 being a perfect match.\n\n#### Parameters\n\n* **url** or **image** *(required)*\n\n  The image to add to the database. It may be provided as a URL via `url` or as a `multipart/form-data` file upload via `image`.\n\n* **all_orientations** *(default: true)*\n\n  Whether or not to search for similar 90 degree rotations of the image.\n\n#### Example Response\n\n```json\n{\n  \"status\": \"ok\",\n  \"error\": [],\n  \"method\": \"search\",\n  \"result\": [\n    {\n      \"score\": 99.0,\n      \"filepath\": \"http://static.wixstatic.com/media/0149b5_345c8f862e914a80bcfcc98fcd432e97.jpg_srz_614_709_85_22_0.50_1.20_0.00_jpg_srz\"\n    }\n  ]\n}\n```\n\n---\n\n### POST `/compare`\n\nCompares two images, returning a score for their similarity. Scores range from 0 to 100, with 100 being a perfect match.\n\n#### Parameters\n\n* **url1** or **image1**, **url2** or **image2** *(required)*\n\n  The images to compare. They may be provided as a URL via `url1`/`url2` or as a `multipart/form-data` file upload via `image1`/`image2`.\n\n#### Example Response\n\n```json\n{\n  \"status\": \"ok\",\n  \"error\": [],\n  \"method\": \"compare\",\n  \"result\": [\n    {\n      \"score\": 99.0\n    }\n  ]\n}\n```\n\n---\n\n### GET `/count`\n\nCount the number of image signatures in the database.\n\n#### Example Response\n\n```json\n{\n  \"status\": \"ok\",\n  \"error\": [],\n  \"method\": \"list\",\n  \"result\": [420]\n}\n```\n\n---\n\n### GET `/list`\n\nLists the file paths for the image signatures in the database.\n\n#### Parameters\n\n* **offset** *(default: 0)*\n\n  The location in the database to begin listing image paths.\n\n* **limit** *(default: 20)*\n\n  The number of image paths to retrieve.\n\n#### Example Response\n\n```json\n{\n  \"status\": \"ok\",\n  \"error\": [],\n  \"method\": \"list\",\n  \"result\": [\n    \"http://img.youtube.com/vi/iqPqylKy-bY/0.jpg\",\n    \"https://i.ytimg.com/vi/zbjIwBggt2k/hqdefault.jpg\",\n    \"https://s-media-cache-ak0.pinimg.com/736x/3d/67/6d/3d676d3f7f3031c9fd91c10b17d56afe.jpg\"\n  ]\n}\n```\n\n---\n\n### GET `/ping`\n\nCheck for the health of the server.\n\n#### Example Response\n\n```json\n{\n  \"status\": \"ok\",\n  \"error\": [],\n  \"method\": \"ping\",\n  \"result\": []\n}\n```\n\n## Development\n\n    $ export ELASTICSEARCH_URL=https://daisy.us-west-1.es.amazonaws.com\n    $ make build\n    $ make run\n    $ make push\n\n## License and Acknowledgements\n\nMatch is based on [ascribe/image-match](https://github.com/ascribe/image-match), which is in turn based on the paper [_An image signature for any kind of image_, Goldberg et al](http://www.cs.cmu.edu/~hcwong/Pdfs/icip02.ps). There is an existing [reference implementation](https://www.pureftpd.org/project/libpuzzle) which may be more suited to your needs.\n\nMatch itself is released under the [BSD 3-Clause license](https://github.com/dsys/match/blob/master/LICENSE). `ascribe/image-match` is released under the Apache 2.0 license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsys%2Fmatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdsys%2Fmatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsys%2Fmatch/lists"}