{"id":18010888,"url":"https://github.com/yokawasa/facetag-services","last_synced_at":"2025-04-04T13:15:54.150Z","repository":{"id":66860524,"uuid":"207755768","full_name":"yokawasa/facetag-services","owner":"yokawasa","description":"[Alpha version] Full serverless face tagging services that allow you to build photo sharing services where you can find your registered persons among uploaded photos easily.","archived":false,"fork":false,"pushed_at":"2019-10-15T12:26:21.000Z","size":1270,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-09T22:45:18.494Z","etag":null,"topics":["azure-functions","cognitive-services","cosmosdb","face-recognition","photos","python","python3","serverless"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yokawasa.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-11T07:56:10.000Z","updated_at":"2024-04-29T19:14:42.000Z","dependencies_parsed_at":"2023-03-11T00:24:19.121Z","dependency_job_id":null,"html_url":"https://github.com/yokawasa/facetag-services","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yokawasa%2Ffacetag-services","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yokawasa%2Ffacetag-services/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yokawasa%2Ffacetag-services/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yokawasa%2Ffacetag-services/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yokawasa","download_url":"https://codeload.github.com/yokawasa/facetag-services/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247182421,"owners_count":20897381,"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":["azure-functions","cognitive-services","cosmosdb","face-recognition","photos","python","python3","serverless"],"created_at":"2024-10-30T02:15:42.589Z","updated_at":"2025-04-04T13:15:54.133Z","avatar_url":"https://github.com/yokawasa.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# facetag-services\n\nFace tagging services is a collection of APIs that allow you to build photo sharing services where you can find your registered persons among uploaded photos easily. It is full serverless services built using Azure functions, CosmosDB, Azure Blob Storage for backend.\n\n**Table of Contents**\n\u003c!-- TOC --\u003e\n\n- [facetag-services](#facetag-services)\n    - [Features](#features)\n    - [Architecture](#architecture)\n    - [Example Operations](#example-operations)\n        - [Training faces of persons](#training-faces-of-persons)\n        - [Uploading photos and identifying faces of persons](#uploading-photos-and-identifying-faces-of-persons)\n    - [REST APIs](#rest-apis)\n        - [Authentication](#authentication)\n        - [Regist User](#regist-user)\n        - [Delete User](#delete-user)\n        - [Create Person](#create-person)\n        - [Delete Person](#delete-person)\n        - [Get Persons](#get-persons)\n        - [Create Asset](#create-asset)\n        - [Delete Asset](#delete-asset)\n        - [Get Assets](#get-assets)\n        - [Get Photos](#get-photos)\n        - [Trigger Train](#trigger-train)\n        - [Get Blob SAS Token](#get-blob-sas-token)\n\n\u003c!-- /TOC --\u003e\n\n## Features\n- Train faces of a person\n- Uploading photos\n- Identify faces of a person in uploaded photos\n- Management APIs for Users, Assets, Persons, and Photos\n\n## Architecture\n**Data flows of major operations**\n![](assets/facetag-services-arch.png)\n\n**Azure functions proxies: URI path and function mapping**\n![](assets/proxies-uri-functions-mapping.png)\n\n**Concept of user, person, and asset in facetag_services**\n\n![](assets/concepts.png)\n\n\n## Example Operations\n### Training faces of persons\n| Operation | Example |\n|---|---|\n| 1. Register user using [Regist User](#regist-user) API (Only if not yet registerd user) | Regist a user with user_id \"nogizaka46\" |\n| 2. Create a person for the user using [Create Person](#create-person) API. The API response contains `person_id` and `asset_id_for_train`. You'll use `person_id` for identifying the person, and `asset_id_for_train` for uploading photos of the person in order to train the face of the person. So please don't forget to get both info! | Create a person named \"Mai Shiraishi\", and get \"063aa8ac-c9b3-4969-952f-26388ab67726\" for `person_id` and \"c4baf03e-1eb0-4dde-b0e6-b1d68e1f5df3\" for `asset_id_for_train` |\n| 3. Upload photos of the person to the prepared container ( container name == `asset_id_for_train` ) in Azure blob storage | Upload photos of a person \"Mai Shiraishi\" to the container named \"c4baf03e-1eb0-4dde-b0e6-b1d68e1f5df3\" (= `asset_id_for_train`, the one you got previously)  |\n| 4. Trigger training of the person's face using [Trigger Train](#trigger-train) API. The training will be done asynchronously as a backgroud job | Trigger the training of a person \"Mai Shiraishi\" (`person_id`: \"063aa8ac-c9b3-4969-952f-26388ab67726\")'s faces |\n\n### Uploading photos and identifying faces of persons \n\n| Operation | Example |\n|---|---|\n| 1. Create an asset for a certain category of photos that you want to upload using [Create Asset](#create-asset) API. The API response contains `asset_id` for the created asset. You'll use the `asset_id` for uploading photos of a certain category | Create an asset named \"Album 2019\" and you'll get an asset_id \"52edcedb-f4c8-4ef5-8e16-9b56ab28346b\" for the asset. |\n| 2. Upload photos of the certain cateogry to the prepared container ( container name == asset_id) in Azure blob storage. As soon as the photos are uploaded, event-driven functions are triggerred to run in order to detect, identify, and tag faces of the persons that you created/trained. Identified face info is stored persistently in backend databases.  | |\n\n## REST APIs\n### Authentication\nAdd API Key for functions in HTTP header in REST API requests\n\n```json\nx-functions-key: {API_KEY}\n```\n\n\u003e Example request for getperson API: \n```bash\nAPI_KEY=\"abcdefgxxxxxxxxxyyyyyyyyzzzzzzzz******==\"\ncurl -s \\\n -H \"Content-Type: application/json\"\\\n -H \"x-functions-key: ${API_KEY}\"\\\n -XPOST https://myfacetagsvc.azurewebsites.net/person -d'\n{\n  \"person_name\": \"Mai Shiaishi\"\n}'\n```\n\n### Regist User \nRegist a user \n\n\u003e POST /user\n\n**Request**\n\n|Name|Type| Requred |Description|\n|---|---|---|---|\n| `user_id`| string | Yes | User ID. It has to be unique across the whole  service. Only allow only alphanumeric and underscore in user_id. |\n| `user_name`| string |  | Name of User |\n\nExample Body\n```json\n{\n  \"user_id\": \"nogizaka46\",\n  \"user_name\": \"Nogizaka 46 Group\"\n}\n```\n\n**Response**\n\n```\nStatus: 200 OK\n```\n```\n\u003cuser_id\u003e\n```\n\n### Delete User\nDelete a user \n\n\u003e DELETE /user/{user_id}\n\n**Response**\n\n```\nStatus: 200 OK\n```\n```\n\u003cuser_id\u003e\n```\n\n### Create Person\nCreate a new person for a user. A created person ID will be returned as reseponse body.\n\n\u003e POST /user/{user_id}/person\n\n**Request**\n\n|Name|Type| Requred |Description|\n|---|---|---|---|\n| `person_name`| string |  | Name of Person |\n\nExample Body\n```json\n{\n  \"person_name\": \"Mai Shiraishi\"\n}\n```\n\n**Response**\n\n```\nStatus: 200 OK\n```\n```json\n{\n   \"person_id\": \"063aa8ac-c9b3-4969-952f-26388ab67726\",\n   \"person_name\": \"Mai Shiraishi\",\n   \"asset_id_for_train\": \"c4baf03e-1eb0-4dde-b0e6-b1d68e1f5df3\"\n}\n```\n\n### Delete Person\nDelete person from a person group of a user\n\n\u003e DELETE /api/deleteperson?user_id={user_id}\u0026person_id={person_id}\n**Response**\n\n```\nStatus: 200 OK\n```\n```\n\u003cdeleted_person_id\u003e\n```\n\n### Get Persons\nGet the list of persons for a user\n\n\u003e GET /user/{user_id}/persons\n\n**Response**\n\n```\nStatus: 200 OK\n```\n```json\n[\n  {\n    \"person_id\": \"a654f4c2-dc7d-43dc-a95a-8819da69587a\",\n    \"person_name\": \"Mai Shiraishi\",\n    \"asset_id_for_train\": \"4119ad84-2e9d-463e-8c66-1f049fa4afc1\"\n  },\n  {\n    \"person_id\": \"3dad91b4-fa4c-4c11-b9c0-e240a579c253\",\n    \"person_name\": \"Erika Ikuta\",\n    \"asset_id_for_train\": \"ef57792e-ec97-4584-8090-7d2f3eb680cd\"\n  },  \n  {\n    \"person_id\": \"25eba549-fa9e-493d-a754-af2eb1aff735\",\n    \"person_name\": \"Manatsu Akimoto\",\n    \"asset_id_for_train\": \"b89387bf-5a0e-48a2-bf58-49b6f1f78982\"\n  }, \n  ...\n]\n```\n\n### Create Asset\nCreate an asset of a user\n\nCreate a new asset for a user. A created asset ID will be returned as reseponse body.\n\n\u003e POST /user/{user_id}/asset\n\n**Request**\n\n|Name|Type| Requred |Description|\n|---|---|---|---|\n| `asset_name`| string |  | Name of asset |\n\nExample Body\n```json\n{\n  \"asset_name\": \"Album 2019\"\n}\n```\n\n**Response**\n\n```\nStatus: 200 OK\n```\n```\n\u003ccreated_asset_id\u003e\n```\n\n### Delete Asset\nDelete an asset of a user\n\n\u003e DELETE /api/deleteasset?user_id={user_id}\u0026asset_id={asset_id}\n\n**Response**\n\n```\nStatus: 200 OK\n```\n```\n\u003cdeleted_asset_id\u003e\n```\n\n### Get Assets\nGet the list of assets of a user\n\n\u003e GET /user/{user_id}/assets\n\n**Response**\n\n```\nStatus: 200 OK\n```\n```json\n[\n  {\n    \"asset_id\": \"5x472930-x3c6-55df-93x2-z9900a2b2300\",\n    \"asset_name\": \"Album 2017\",\n    \"user_id\": \"nogizaka46\"\n  },  \n  {\n    \"asset_id\": \"2d447b84-e2c4-4eed-93b6-e6600a2b5608\",\n    \"asset_name\": \"Album 2018\",\n    \"user_id\": \"nogizaka46\"\n  },\n  {\n    \"asset_id\": \"2b48152d-02b1-443f-9617-eaa499fb0a93\",\n    \"asset_name\": \"Album 2019\",\n    \"user_id\": \"nogizaka46\"\n  },\n  ...\n]\n```\n\n### Get Photos\nGet the list of photos of a user. You can filter photos by a Person ID, Asset ID, and a User ID.\n\n\u003e POST /photos\n\n**Request**\n\n|Name|Type| Requred |Description|\n|---|---|---|---|\n| `user_id`| string | Yes | User ID |\n| `person_id`| string |  | Person ID |\n| `asset_id`| string |  | Asset ID |\n| `order`| string |  | ASC or DESC (default) |\n| `offset`| number |  | Offset position of result items(Default 0) |\n| `limit`| number |  | limit number of result items (Default 100) |\n\nExample 1: Get photos of a person in a user group (user_id + person_id)\n```json\n{\n  \"user_id\": \"nogizaka46\",\n  \"person_id\": \"a654f4c2-dc7d-43dc-a95a-8819da69587a\",\n  \"order\": \"DESC\",\n  \"offset\": \"0\",\n  \"limit\": \"50\"\n}\n```\n\nExample 2: Get photos stored in a asset (user_id + asset_id)\n```json\n{\n  \"user_id\": \"nogizaka46\",\n  \"person_id\": \"a654f4c2-dc7d-43dc-a95a-8819da69587a\",\n  \"order\": \"DESC\",\n  \"offset\": \"0\",\n  \"limit\": \"50\"\n}\n```\n\nExample 3: Get photos in a user group (user_id)\n```json\n{\n  \"user_id\": \"nogizaka46\",\n  \"order\": \"DESC\",\n  \"offset\": \"0\",\n  \"limit\": \"50\"\n}\n```\n\n**Response**\n\n```\nStatus: 200 OK\n```\n```json\n[\n  {\n    \"photo_id\": \"114a2d4a36340e0726e1079145d8e8c6794b8b7541c334edae997691248b01e7\",\n    \"asset_id\": \"2d447b84-e2c4-4eed-93b6-e6600a2b5608\",\n    \"blob_name\": \"mai-shiraishi-0.jpg\",\n    \"user_id\": \"nogizaka46\",\n    \"persons\": [\n      {\n        \"person_id\": \"a654f4c2-dc7d-43dc-a95a-8819da69587a\"\n      }\n    ],\n    \"blob_url\": \"https://facetagservice.blob.core.windows.net/ef57792e-ec97-4584-8090-7d2f3eb680cd/mai-shiraishi-0.jpg?sv=2018-03-28\u0026ss=b\u0026srt=o\u0026sp=rl\u0026se=2019-10-12T08%3A01%3A53Z\u0026st=2019-10-12T06%3A56%3A53Z\u0026spr=https\u0026sig=%2FrxQ7P3UlasoXqk9mfIgr0uRBQQQPmmKhvKJN%2Bteisc%3D\",\n    \"last_updated\": 1569808309\n  },\n  {\n    \"photo_id\": \"4369e41be321894298ed3188f715dc4f14a8ae51d1bcd1dcd21c2dc2ce162af9\",\n    \"asset_id\": \"2d447b84-e2c4-4eed-93b6-e6600a2b5608\",\n    \"blob_name\": \"mai-shiraishi-1.jpg\",\n    \"user_id\": \"nogizaka46\",\n    \"persons\": [\n      {\n        \"person_id\": \"a654f4c2-dc7d-43dc-a95a-8819da69587a\"\n      }\n    ],\n    \"blob_url\": \"https://facetagservice.blob.core.windows.net/ef57792e-ec97-4584-8090-7d2f3eb680cd/mai-shiraishi-1.jpg?sv=2018-03-28\u0026ss=b\u0026srt=o\u0026sp=rl\u0026se=2019-10-12T08%3A01%3A53Z\u0026st=2019-10-12T06%3A56%3A53Z\u0026spr=https\u0026sig=%2FrxQ7P3UlasoXqk9mfIgr0uRBQQQPmmKhvKJN%2Bteisc%3D\",\n    \"last_updated\": 1569808315\n  },\n  {\n    \"photo_id\": \"0321b08d15c1065569b0bcda5bc726ea9473b3107f78d15c323b0bf621add9f4\",\n    \"asset_id\": \"2d447b84-e2c4-4eed-93b6-e6600a2b5608\",\n    \"blob_name\": \"nobody-0.jpg\",\n    \"user_id\": \"nogizaka46\",\n    \"persons\": [],\n    \"blob_url\": \"https://facetagservice.blob.core.windows.net/ef57792e-ec97-4584-8090-7d2f3eb680cd/nobody-0.jpg?sv=2018-03-28\u0026ss=b\u0026srt=o\u0026sp=rl\u0026se=2019-10-12T08%3A01%3A53Z\u0026st=2019-10-12T06%3A56%3A53Z\u0026spr=https\u0026sig=%2FrxQ7P3UlasoXqk9mfIgr0uRBQQQPmmKhvKJN%2Bteisc%3D\",\n    \"last_updated\": 1569808340\n  }\n  ...\n]\n```\n\n### Trigger Train\n\nTrigger training of a person's faces\n\n\u003e PUT /user/{user_id}/person/{person_id}/trigger\n\n**Request**\n\n|Name|Type| Requred |Description|\n|---|---|---|---|\n| `user_id`| string | Yes | User ID |\n| `person_id`| string | Yes | Person ID |\n\n**Response**\n\n```\nStatus: 200 OK\n```\n\n### Get Blob SAS Token\n\nGet a SAS token for Azure Storage for the specified container and blob name. You can also specify access permissions for the container/blob name and optionally its token time-to-live period. The SAS token expires in an hour by default.\n\n\u003e POST /blobsastoken\n\n**Request**\n\n|Name|Type| Requred |Description|\n|---|---|---|---|\n|`permission`| string | Yes | Signed permission for shared access signature. See Note below for the detai) | \n|`container` | string | Yes | Container name to access |\n|`blobname`  | string | | Blob object name to access |\n| `ttl`      | string | | Token time to live period in hours. 1hour by default |\n\n\u003e NOTE for **permission** param:\n\u003e - The following values can be used for permissions: \n\u003e  `a` (Add), `r` (Read), `w` (Write), `d` (Delete), `l` (List)\n\u003e - Concatenate multiple permissions, such as `rwa` = Read, Write, Add\n\nSample Request Body\n```json\n {\n    'permission': \"rl\",\n    'container': \"functions\",\n    'blobname': \"yokawasa.png\"\n }\n```\n**Response** \nResponse body format\n```json\nHTTP response body format is:\n{\n    'token': '\u003cShared Access Signature Token string\u003e',\n    'url' :  '\u003cSAS resource URI\u003e'\n}\n```\n\nSample Response Body\n```json\n{\n  \"token\": \"sv=2018-03-28\u0026ss=b\u0026srt=o\u0026sp=rl\u0026se=2019-03-29T14%3A02%3A37Z\u0026st=2019-03-29T11%3A57%3A37Z\u0026spr=https\u0026sig=Sh7RAa5MZBk7gfv0haCbEbllFXoiOWJDK9itzPeqURE%3D\",\n  \"url\": \"https://azfuncv2linuxstore.blob.core.windows.net/functiontest/sample.jpg?sv=2018-03-28\u0026ss=b\u0026srt=o\u0026sp=rl\u0026se=2019-03-29T14%3A02%3A37Z\u0026st=2019-03-29T11%3A57%3A37Z\u0026spr=https\u0026sig=Sh7RAa5MZBk7gfv0haCbEbllFXoiOWJDK9itzPeqURE%3D\" \n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyokawasa%2Ffacetag-services","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyokawasa%2Ffacetag-services","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyokawasa%2Ffacetag-services/lists"}