Quellcode durchsuchen

Implemented image tagging using TensorFlow InceptionV3 (#28)

* Refactor docker-compose to its own folder
* Added FastAPI development environment
* Added support for GPU in docker file
* Added image classification
* creating endpoint for smart Image info
* added logo with white background on ios
* Added endpoint and trigger for image tagging
* Classify image and save into database
* Update readme
Alex vor 3 Jahren
Ursprung
Commit
619735fea0
54 geänderte Dateien mit 2262 neuen und 10636 gelöschten Zeilen
  1. 3 3
      Makefile
  2. 7 5
      README.md
  3. 0 0
      docker/.env.example
  4. 1 0
      docker/.gitignore
  5. 95 0
      docker/docker-compose.gpu.yml
  6. 31 10
      docker/docker-compose.yml
  7. 35 0
      docker/settings/nginx-conf/nginx.conf
  8. 1 0
      machine_learning/.dockerignore
  9. 2 0
      machine_learning/.gitignore
  10. 22 0
      machine_learning/Dockerfile
  11. 17 0
      machine_learning/Pipfile
  12. 629 0
      machine_learning/Pipfile.lock
  13. 1002 0
      machine_learning/app/imagenet_class_index.json
  14. 51 0
      machine_learning/app/main.py
  15. 0 0
      machine_learning/app/test.png
  16. 6 0
      machine_learning/requirements.txt
  17. 95 59
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  18. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
  19. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
  20. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
  21. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
  22. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
  23. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-1024.png
  24. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-20.png
  25. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-20@2x.png
  26. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-20@3x.png
  27. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-29.png
  28. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-29@2x.png
  29. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-29@3x.png
  30. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-40.png
  31. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-40@2x.png
  32. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-40@3x.png
  33. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-60@2x.png
  34. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-60@3x.png
  35. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-76.png
  36. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-76@2x.png
  37. BIN
      mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-83.5@2x.png
  38. 2 0
      server/entrypoint.sh
  39. 1 10530
      server/package-lock.json
  40. 5 1
      server/package.json
  41. 4 0
      server/src/api-v1/asset/entities/asset.entity.ts
  42. 19 0
      server/src/api-v1/asset/entities/smart-info.entity.ts
  43. 11 15
      server/src/config/database.config.ts
  44. 12 4
      server/src/middlewares/redis-io.adapter.middleware.ts
  45. 22 0
      server/src/migration/1645130759468-CreateUserTable.ts
  46. 26 0
      server/src/migration/1645130777674-CreateDeviceInfoTable.ts
  47. 31 0
      server/src/migration/1645130805273-CreateAssetsTable.ts
  48. 42 0
      server/src/migration/1645130817965-CreateExifTable.ts
  49. 30 0
      server/src/migration/1645130870184-CreateSmartInfoTable.ts
  50. 2 1
      server/src/modules/background-task/background-task.module.ts
  51. 19 0
      server/src/modules/background-task/background-task.processor.ts
  52. 11 0
      server/src/modules/background-task/background-task.service.ts
  53. 12 8
      server/src/modules/image-optimize/image-optimize.module.ts
  54. 16 0
      server/src/modules/image-optimize/image-optimize.processor.ts

+ 3 - 3
Makefile

@@ -1,8 +1,8 @@
 dev:
-	docker-compose -f ./server/docker-compose.yml up
+	docker-compose -f ./docker/docker-compose.yml up
 
 dev-update:
-	docker-compose -f ./server/docker-compose.yml up --build -V 
+	docker-compose -f ./docker/docker-compose.yml up --build -V 
 
 dev-scale:
-	docker-compose -f ./server/docker-compose.yml up --build -V  --scale immich_server=3 --remove-orphans 
+	docker-compose -f ./docker/docker-compose.yml up --build -V  --scale immich_server=3 --remove-orphans 

+ 7 - 5
README.md

@@ -44,13 +44,15 @@ You can use docker compose for development, there are several services that comp
 2. PostgreSQL
 3. Redis
 4. Nginx
+5. TensorFlow and Keras
 
 ## Populate .env file
 
-Navigate to `server` directory and run
+Navigate to `docker` directory and run
 
-````
+```
 cp .env.example .env
+```
 
 Then populate the value in there.
 
@@ -59,13 +61,13 @@ Pay attention to the key `UPLOAD_LOCATION`, this directory must exist and is own
 To start, run
 
 ```bash
-docker-compose -f ./server/docker-compose.yml up
-````
+docker-compose -f ./docker/docker-compose.yml up
+```
 
 To force rebuild node modules after installing new packages
 
 ```bash
-docker-compose -f ./server/docker-compose.yml up --build -V
+docker-compose -f ./docker/docker-compose.yml up --build -V
 ```
 
 The server will be running at `http://your-ip:2283` through `Nginx`

+ 0 - 0
server/.env.example → docker/.env.example


+ 1 - 0
docker/.gitignore

@@ -0,0 +1 @@
+.env

+ 95 - 0
docker/docker-compose.gpu.yml

@@ -0,0 +1,95 @@
+version: "3.8"
+
+services:
+  immich_server:
+    image: immich-server-dev:1.0.0
+    build:
+      context: ../server
+      target: development
+      dockerfile: ../server/Dockerfile
+    command: npm run start:dev
+    expose:
+      - "3000"
+    volumes:
+      - ../server:/usr/src/app
+      - ${UPLOAD_LOCATION}:/usr/src/app/upload
+      - /usr/src/app/node_modules
+    env_file:
+      - .env
+    depends_on:
+      - redis
+      - database
+    networks:
+      - immich_network
+
+  redis:
+    container_name: immich_redis
+    image: redis:6.2
+    networks:
+      - immich_network
+
+  database:
+    container_name: immich_postgres
+    image: postgres:14
+    env_file:
+      - .env
+    environment:
+      POSTGRES_PASSWORD: ${DB_PASSWORD}
+      POSTGRES_USER: ${DB_USERNAME}
+      POSTGRES_DB: ${DB_DATABASE_NAME}
+      PG_DATA: /var/lib/postgresql/data
+    volumes:
+      - pgdata:/var/lib/postgresql/data
+    ports:
+      - 5432:5432
+    networks:
+      - immich_network
+
+  nginx:
+    container_name: proxy_nginx
+    image: nginx:latest
+    volumes:
+      - ./settings/nginx-conf:/etc/nginx/conf.d
+    ports:
+      - 2283:80
+      - 2284:443
+    logging:
+      driver: none
+    networks:
+      - immich_network
+    depends_on:
+      - immich_server
+
+  immich_tf_fastapi:
+    container_name: immich_tf_fastapi
+    image: tensor_flow_fastapi:1.0.0
+    restart: always
+    command: uvicorn app.main:app --proxy-headers --host 0.0.0.0 --port 8000 --reload
+    build:
+      context: ../machine_learning
+      target: gpu
+      dockerfile: ../machine_learning/Dockerfile
+    deploy:
+      resources:
+        reservations:
+          devices:
+            - driver: nvidia
+              count: 1
+              capabilities: [gpu]
+    volumes:
+      - ../machine_learning/app:/code/app
+      - ${UPLOAD_LOCATION}:/code/app/upload
+    ports:
+      - 2285:8000
+    expose:
+      - "8000"
+    depends_on:
+      - database
+    networks:
+      - immich_network
+
+      
+networks:
+  immich_network:
+volumes:
+  pgdata:

+ 31 - 10
server/docker-compose.yml → docker/docker-compose.yml

@@ -1,18 +1,18 @@
-version: '3.8'
-
+version: "3.8"
 
 services:
   immich_server:
     image: immich-server-dev:1.0.0
     build:
-      context: .
+      context: ../server
       target: development
-      dockerfile: ./Dockerfile
-    command: npm run start:dev
-    expose: 
+      dockerfile: ../server/Dockerfile
+    entrypoint: ["/bin/sh", "./entrypoint.sh"]
+    # command: npm run start:dev
+    expose:
       - "3000"
     volumes:
-      - .:/usr/src/app
+      - ../server:/usr/src/app
       - ${UPLOAD_LOCATION}:/usr/src/app/upload
       - /usr/src/app/node_modules
     env_file:
@@ -27,7 +27,7 @@ services:
     container_name: immich_redis
     image: redis:6.2
     networks:
-      - immich_network  
+      - immich_network
 
   database:
     container_name: immich_postgres
@@ -44,7 +44,7 @@ services:
     ports:
       - 5432:5432
     networks:
-      - immich_network  
+      - immich_network
 
   nginx:
     container_name: proxy_nginx
@@ -61,7 +61,28 @@ services:
     depends_on:
       - immich_server
 
+  immich_tf_fastapi:
+    container_name: immich_tf_fastapi
+    image: tensor_flow_fastapi:1.0.0
+    restart: always
+    command: uvicorn app.main:app --proxy-headers --host 0.0.0.0 --port 8000 --reload
+    build:
+      context: ../machine_learning
+      target: cpu
+      dockerfile: ../machine_learning/Dockerfile
+    volumes:
+      - ../machine_learning/app:/code/app
+      - ${UPLOAD_LOCATION}:/code/app/upload
+    ports:
+      - 2285:8000
+    expose:
+      - "8000"
+    depends_on:
+      - database
+    networks:
+      - immich_network
+
 networks:
   immich_network:
 volumes:
-  pgdata:
+  pgdata:

+ 35 - 0
docker/settings/nginx-conf/nginx.conf

@@ -0,0 +1,35 @@
+
+map $http_upgrade $connection_upgrade {
+  default upgrade;
+  '' close;
+}
+
+# events {
+#   worker_connections 1000;
+# }
+
+server {
+
+  client_max_body_size 50000M;
+
+  listen 80;
+
+  location / {
+    proxy_buffering off;
+    proxy_buffer_size 16k;
+    proxy_busy_buffers_size 24k;
+    proxy_buffers 64 4k;
+    proxy_force_ranges on;
+
+    proxy_http_version 1.1;
+    proxy_set_header Host $host;
+    proxy_set_header X-Real-IP $remote_addr;
+    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    proxy_set_header X-Forwarded-Proto $scheme;
+    proxy_set_header Upgrade $http_upgrade;
+    proxy_set_header Connection "upgrade";
+    proxy_set_header Host $host;
+
+    proxy_pass http://immich_server:3000;
+  }
+}

+ 1 - 0
machine_learning/.dockerignore

@@ -0,0 +1 @@
+devenv/

+ 2 - 0
machine_learning/.gitignore

@@ -0,0 +1,2 @@
+app/__pycache__
+/devenv

+ 22 - 0
machine_learning/Dockerfile

@@ -0,0 +1,22 @@
+## GPU Build
+FROM tensorflow/tensorflow:latest-gpu as gpu
+
+WORKDIR /code
+
+COPY ./requirements.txt /code/requirements.txt
+
+RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
+
+COPY ./app /code/app
+
+
+## CPU BUILD
+FROM python:3.8 as cpu
+
+WORKDIR /code
+
+COPY ./requirements.txt /code/requirements.txt
+
+RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
+
+COPY ./app /code/app

+ 17 - 0
machine_learning/Pipfile

@@ -0,0 +1,17 @@
+[[source]]
+url = "https://pypi.python.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+fastapi = "<0.69.0,>=0.68.0"
+pydantic = "<2.0.0,>=1.8.0"
+uvicorn = "<0.16.0,>=0.15.0"
+tensorflow = "~=2.8.0"
+numpy = "==1.22.2"
+pillow = "==9.0.1"
+
+[dev-packages]
+
+[requires]
+python_version = "3.8"

+ 629 - 0
machine_learning/Pipfile.lock

@@ -0,0 +1,629 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "3acf9bcf1b74370cbdac742fee64295572085d1c0d3e4ba38b0fc3ae2c7d846a"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.8"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.python.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "absl-py": {
+            "hashes": [
+                "sha256:84e6dcdc69c947d0c13e5457d056bd43cade4c2393dce00d684aedea77ddc2a3",
+                "sha256:ac511215c01ee9ae47b19716599e8ccfa746f2e18de72bdf641b79b22afa27ea"
+            ],
+            "version": "==1.0.0"
+        },
+        "asgiref": {
+            "hashes": [
+                "sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0",
+                "sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9"
+            ],
+            "version": "==3.5.0"
+        },
+        "astunparse": {
+            "hashes": [
+                "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872",
+                "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"
+            ],
+            "version": "==1.6.3"
+        },
+        "cachetools": {
+            "hashes": [
+                "sha256:486471dfa8799eb7ec503a8059e263db000cdda20075ce5e48903087f79d5fd6",
+                "sha256:8fecd4203a38af17928be7b90689d8083603073622229ca7077b72d8e5a976e4"
+            ],
+            "version": "==5.0.0"
+        },
+        "certifi": {
+            "hashes": [
+                "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872",
+                "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"
+            ],
+            "version": "==2021.10.8"
+        },
+        "charset-normalizer": {
+            "hashes": [
+                "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
+                "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
+            ],
+            "markers": "python_version >= '3'",
+            "version": "==2.0.12"
+        },
+        "click": {
+            "hashes": [
+                "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3",
+                "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"
+            ],
+            "version": "==8.0.3"
+        },
+        "fastapi": {
+            "hashes": [
+                "sha256:36bcdd3dbea87c586061005e4a40b9bd0145afd766655b4e0ec1d8870b32555c",
+                "sha256:38526fc46bda73f7ec92033952677323c16061e70a91d15c95f18b11895da494"
+            ],
+            "index": "pypi",
+            "version": "==0.68.2"
+        },
+        "flatbuffers": {
+            "hashes": [
+                "sha256:12158ab0272375eab8db2d663ae97370c33f152b27801fa6024e1d6105fd4dd2",
+                "sha256:3751954f0604580d3219ae49a85fafec9d85eec599c0b96226e1bc0b48e57474"
+            ],
+            "version": "==2.0"
+        },
+        "gast": {
+            "hashes": [
+                "sha256:211aac1e58c167b25d3504998f2db694454a24bb1fb1225bce99420166f21d6a",
+                "sha256:cfbea25820e653af9c7d1807f659ce0a0a9c64f2439421a7bba4f0983f532dea"
+            ],
+            "version": "==0.5.3"
+        },
+        "google-auth": {
+            "hashes": [
+                "sha256:218ca03d7744ca0c8b6697b6083334be7df49b7bf76a69d555962fd1a7657b5f",
+                "sha256:ad160fc1ea8f19e331a16a14a79f3d643d813a69534ba9611d2c80dc10439dad"
+            ],
+            "version": "==2.6.0"
+        },
+        "google-auth-oauthlib": {
+            "hashes": [
+                "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73",
+                "sha256:a90a072f6993f2c327067bf65270046384cda5a8ecb20b94ea9a687f1f233a7a"
+            ],
+            "version": "==0.4.6"
+        },
+        "google-pasta": {
+            "hashes": [
+                "sha256:4612951da876b1a10fe3960d7226f0c7682cf901e16ac06e473b267a5afa8954",
+                "sha256:b32482794a366b5366a32c92a9a9201b107821889935a02b3e51f6b432ea84ed",
+                "sha256:c9f2c8dfc8f96d0d5808299920721be30c9eec37f2389f28904f454565c8a16e"
+            ],
+            "version": "==0.2.0"
+        },
+        "grpcio": {
+            "hashes": [
+                "sha256:0110310eff07bb69782f53b7a947490268c4645de559034c43c0a635612e250f",
+                "sha256:01f4b887ed703fe82ebe613e1d2dadea517891725e17e7a6134dcd00352bd28c",
+                "sha256:04239e8f71db832c26bbbedb4537b37550a39d77681d748ab4678e58dd6455d6",
+                "sha256:08cf25f2936629db062aeddbb594bd76b3383ab0ede75ef0461a3b0bc3a2c150",
+                "sha256:0aa8285f284338eb68962fe1a830291db06f366ea12f213399b520c062b01f65",
+                "sha256:0e731f660e1e68238f56f4ce11156f02fd06dc58bc7834778d42c0081d4ef5ad",
+                "sha256:0edbfeb6729aa9da33ce7e28fb7703b3754934115454ae45e8cc1db601756fd3",
+                "sha256:124e718faf96fe44c98b05f3f475076be8b5198bb4c52a13208acf88a8548ba9",
+                "sha256:138f57e3445d4a48d9a8a5af1538fdaafaa50a0a3c243f281d8df0edf221dc02",
+                "sha256:17b75f220ee6923338155b4fcef4c38802b9a57bc57d112c9599a13a03e99f8d",
+                "sha256:1898f999383baac5fcdbdef8ea5b1ef204f38dc211014eb6977ac6e55944d738",
+                "sha256:1f16725a320460435a8a5339d8b06c4e00d307ab5ad56746af2e22b5f9c50932",
+                "sha256:2f96142d0abc91290a63ba203f01649e498302b1b6007c67bad17f823ecde0cf",
+                "sha256:31e6e489ccd8f08884b9349a39610982df48535881ec34f05a11c6e6b6ebf9d0",
+                "sha256:45401d00f2ee46bde75618bf33e9df960daa7980e6e0e7328047191918c98504",
+                "sha256:47b6821238d8978014d23b1132713dac6c2d72cbb561cf257608b1673894f90a",
+                "sha256:4b4a7152187a49767a47d1413edde2304c96f41f7bc92cc512e230dfd0fba095",
+                "sha256:50cfb7e1067ee5e00b8ab100a6b7ea322d37ec6672c0455106520b5891c4b5f5",
+                "sha256:5449ae564349e7a738b8c38583c0aad954b0d5d1dd3cea68953bfc32eaee11e3",
+                "sha256:577e024c8dd5f27cd98ba850bc4e890f07d4b5942e5bc059a3d88843a2f48f66",
+                "sha256:57f1aeb65ed17dfb2f6cd717cc109910fe395133af7257a9c729c0b9604eac10",
+                "sha256:594aaa0469f4fca7773e80d8c27bf1298e7bbce5f6da0f084b07489a708f16ab",
+                "sha256:6620a5b751b099b3b25553cfc03dfcd873cda06f9bb2ff7e9948ac7090e20f05",
+                "sha256:6e463b4aa0a6b31cf2e57c4abc1a1b53531a18a570baeed39d8d7b65deb16b7e",
+                "sha256:735d9a437c262ab039d02defddcb9f8f545d7009ae61c0114e19dda3843febe5",
+                "sha256:772b943f34374744f70236bbbe0afe413ed80f9ae6303503f85e2b421d4bca92",
+                "sha256:77ef653f966934b3bfdd00e4f2064b68880eb40cf09b0b99edfa5ee22a44f559",
+                "sha256:80398e9fb598060fa41050d1220f5a2440fe74ff082c36dda41ac3215ebb5ddd",
+                "sha256:8b2b9dc4d7897566723b77422e11c009a0ebd397966b165b21b89a62891a9fdf",
+                "sha256:a4b4543e13acb4806917d883d0f70f21ba93b29672ea81f4aaba14821aaf9bb0",
+                "sha256:a4e786a8ee8b30b25d70ee52cda6d1dbba2a8ca2f1208d8e20ed8280774f15c8",
+                "sha256:ade8b79a6b6aea68adb9d4bfeba5d647667d842202c5d8f3ba37ac1dc8e5c09c",
+                "sha256:af78ac55933811e6a25141336b1f2d5e0659c2f568d44d20539b273792563ca7",
+                "sha256:af9c3742f6c13575c0d4147a8454da0ff5308c4d9469462ff18402c6416942fe",
+                "sha256:b8cc936a29c65ab39714e1ba67a694c41218f98b6e2a64efb83f04d9abc4386b",
+                "sha256:bdf41550815a831384d21a498b20597417fd31bd084deb17d31ceb39ad9acc79",
+                "sha256:c354017819201053d65212befd1dcb65c2d91b704d8977e696bae79c47cd2f82",
+                "sha256:c36f418c925a41fccada8f7ae9a3d3e227bfa837ddbfddd3d8b0ac252d12dda9",
+                "sha256:cbc9b83211d905859dcf234ad39d7193ff0f05bfc3269c364fb0d114ee71de59",
+                "sha256:e95b5d62ec26d0cd0b90c202d73e7cb927c369c3358e027225239a4e354967dc",
+                "sha256:f11d05402e0ac3a284443d8a432d3dfc76a6bd3f7b5858cddd75617af2d7bd9b",
+                "sha256:fa26a8bbb3fe57845acb1329ff700d5c7eaf06414c3e15f4cb8923f3a466ef64",
+                "sha256:fb7229fa2a201a0c377ff3283174ec966da8f9fd7ffcc9a92f162d2e7fc9025b",
+                "sha256:fdac966699707b5554b815acc272d81e619dd0999f187cd52a61aef075f870ee"
+            ],
+            "version": "==1.43.0"
+        },
+        "h11": {
+            "hashes": [
+                "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06",
+                "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442"
+            ],
+            "version": "==0.13.0"
+        },
+        "h5py": {
+            "hashes": [
+                "sha256:1c5acc660c458421e88c4c5fe092ce15923adfac4c732af1ac4fced683a5ea97",
+                "sha256:35ab552c6f0a93365b3cb5664a5305f3920daa0a43deb5b2c547c52815ec46b9",
+                "sha256:542781d50e1182b8fb619b1265dfe1c765e18215f818b0ab28b2983c28471325",
+                "sha256:5996ff5adefd2d68c330a4265b6ef92e51b2fc674834a5990add5033bf109e20",
+                "sha256:8752d2814a92aba4e2b2a5922d2782d0029102d99caaf3c201a566bc0b40db29",
+                "sha256:8ecedf16c613973622a334701f67edcc0249469f9daa0576e994fb20ac0405db",
+                "sha256:954c5c39a09b5302f69f752c3bbf165d368a65c8d200f7d5655e0fa6368a75e6",
+                "sha256:98646e659bf8591a2177e12a4461dced2cad72da0ba4247643fd118db88880d2",
+                "sha256:9f39242960b8d7f86f3056cc2546aa3047ff4835985f6483229af8f029e9c8db",
+                "sha256:9fd8a14236fdd092a20c0bdf25c3aba3777718d266fabb0fdded4fcf252d1630",
+                "sha256:a5320837c60870911645e9a935099bdb2be6a786fcf0dac5c860f3b679e2de55",
+                "sha256:c9a5529343a619fea777b7caa27d493595b28b5af8b005e8d1817559fcccf493",
+                "sha256:cd9447633b0bafaf82190d9a8d56f3cb2e8d30169483aee67d800816e028190a",
+                "sha256:d8cacad89aa7daf3626fce106f7f2662ac35b14849df22d252d0d8fab9dc1c0b",
+                "sha256:dbaa1ed9768bf9ff04af0919acc55746e62b28333644f0251f38768313f31745",
+                "sha256:e2b49c48df05e19bb20b400b7ff7dc6f1ee36b84dc717c3771c468b33697b466"
+            ],
+            "version": "==3.6.0"
+        },
+        "idna": {
+            "hashes": [
+                "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
+                "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
+            ],
+            "markers": "python_version >= '3'",
+            "version": "==3.3"
+        },
+        "importlib-metadata": {
+            "hashes": [
+                "sha256:175f4ee440a0317f6e8d81b7f8d4869f93316170a65ad2b007d2929186c8052c",
+                "sha256:e0bc84ff355328a4adfc5240c4f211e0ab386f80aa640d1b11f0618a1d282094"
+            ],
+            "markers": "python_version < '3.10'",
+            "version": "==4.11.1"
+        },
+        "keras": {
+            "hashes": [
+                "sha256:744d39dc6577dcd80ff4a4d41549e92b77d6a17e0edd58a431d30656e29bc94e"
+            ],
+            "version": "==2.8.0"
+        },
+        "keras-preprocessing": {
+            "hashes": [
+                "sha256:7b82029b130ff61cc99b55f3bd27427df4838576838c5b2f65940e4fcec99a7b",
+                "sha256:add82567c50c8bc648c14195bf544a5ce7c1f76761536956c3d2978970179ef3"
+            ],
+            "version": "==1.1.2"
+        },
+        "libclang": {
+            "hashes": [
+                "sha256:069407eac2e20ea8f18212d28c6598db31014e7b8a77febc92e762ec133c3226",
+                "sha256:9c1e623340ccafe3a10a2abbc90f59593ff29f0c854f4ddb65b6220d9d998fb4",
+                "sha256:b61dedc1b941f43acca1fa15df0a6669c6c3983197c6f3226ae03a766281dd37",
+                "sha256:b7de34393ed46c6cf7b22178d0d43cec2f2dab2f5f95450520a47fc1cf2df5ac",
+                "sha256:bcaffec6b1ab9486811670db7af29d4a361830d6cb75da4f5672e884aa973bda",
+                "sha256:dcc7ecd83d91e23e95315d7aa6355ee8d45b43742ca1fb642583e0b2f935d50e"
+            ],
+            "version": "==13.0.0"
+        },
+        "markdown": {
+            "hashes": [
+                "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006",
+                "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3"
+            ],
+            "version": "==3.3.6"
+        },
+        "numpy": {
+            "hashes": [
+                "sha256:03ae5850619abb34a879d5f2d4bb4dcd025d6d8fb72f5e461dae84edccfe129f",
+                "sha256:076aee5a3763d41da6bef9565fdf3cb987606f567cd8b104aded2b38b7b47abf",
+                "sha256:0b536b6840e84c1c6a410f3a5aa727821e6108f3454d81a5cd5900999ef04f89",
+                "sha256:15efb7b93806d438e3bc590ca8ef2f953b0ce4f86f337ef4559d31ec6cf9d7dd",
+                "sha256:168259b1b184aa83a514f307352c25c56af111c269ffc109d9704e81f72e764b",
+                "sha256:2638389562bda1635b564490d76713695ff497242a83d9b684d27bb4a6cc9d7a",
+                "sha256:3556c5550de40027d3121ebbb170f61bbe19eb639c7ad0c7b482cd9b560cd23b",
+                "sha256:4a176959b6e7e00b5a0d6f549a479f869829bfd8150282c590deee6d099bbb6e",
+                "sha256:515a8b6edbb904594685da6e176ac9fbea8f73a5ebae947281de6613e27f1956",
+                "sha256:55535c7c2f61e2b2fc817c5cbe1af7cb907c7f011e46ae0a52caa4be1f19afe2",
+                "sha256:59153979d60f5bfe9e4c00e401e24dfe0469ef8da6d68247439d3278f30a180f",
+                "sha256:60cb8e5933193a3cc2912ee29ca331e9c15b2da034f76159b7abc520b3d1233a",
+                "sha256:6767ad399e9327bfdbaa40871be4254d1995f4a3ca3806127f10cec778bd9896",
+                "sha256:76a4f9bce0278becc2da7da3b8ef854bed41a991f4226911a24a9711baad672c",
+                "sha256:8cf33634b60c9cef346663a222d9841d3bbbc0a2f00221d6bcfd0d993d5543f6",
+                "sha256:94dd11d9f13ea1be17bac39c1942f527cbf7065f94953cf62dfe805653da2f8f",
+                "sha256:aafa46b5a39a27aca566198d3312fb3bde95ce9677085efd02c86f7ef6be4ec7",
+                "sha256:badca914580eb46385e7f7e4e426fea6de0a37b9e06bec252e481ae7ec287082",
+                "sha256:d76a26c5118c4d96e264acc9e3242d72e1a2b92e739807b3b69d8d47684b6677"
+            ],
+            "index": "pypi",
+            "version": "==1.22.2"
+        },
+        "oauthlib": {
+            "hashes": [
+                "sha256:23a8208d75b902797ea29fd31fa80a15ed9dc2c6c16fe73f5d346f83f6fa27a2",
+                "sha256:6db33440354787f9b7f3a6dbd4febf5d0f93758354060e802f6c06cb493022fe"
+            ],
+            "version": "==3.2.0"
+        },
+        "opt-einsum": {
+            "hashes": [
+                "sha256:2455e59e3947d3c275477df7f5205b30635e266fe6dc300e3d9f9646bfcea147",
+                "sha256:59f6475f77bbc37dcf7cd748519c0ec60722e91e63ca114e68821c0c54a46549"
+            ],
+            "version": "==3.3.0"
+        },
+        "pillow": {
+            "hashes": [
+                "sha256:011233e0c42a4a7836498e98c1acf5e744c96a67dd5032a6f666cc1fb97eab97",
+                "sha256:0f29d831e2151e0b7b39981756d201f7108d3d215896212ffe2e992d06bfe049",
+                "sha256:12875d118f21cf35604176872447cdb57b07126750a33748bac15e77f90f1f9c",
+                "sha256:14d4b1341ac07ae07eb2cc682f459bec932a380c3b122f5540432d8977e64eae",
+                "sha256:1c3c33ac69cf059bbb9d1a71eeaba76781b450bc307e2291f8a4764d779a6b28",
+                "sha256:1d19397351f73a88904ad1aee421e800fe4bbcd1aeee6435fb62d0a05ccd1030",
+                "sha256:253e8a302a96df6927310a9d44e6103055e8fb96a6822f8b7f514bb7ef77de56",
+                "sha256:2632d0f846b7c7600edf53c48f8f9f1e13e62f66a6dbc15191029d950bfed976",
+                "sha256:335ace1a22325395c4ea88e00ba3dc89ca029bd66bd5a3c382d53e44f0ccd77e",
+                "sha256:413ce0bbf9fc6278b2d63309dfeefe452835e1c78398efb431bab0672fe9274e",
+                "sha256:5100b45a4638e3c00e4d2320d3193bdabb2d75e79793af7c3eb139e4f569f16f",
+                "sha256:514ceac913076feefbeaf89771fd6febde78b0c4c1b23aaeab082c41c694e81b",
+                "sha256:528a2a692c65dd5cafc130de286030af251d2ee0483a5bf50c9348aefe834e8a",
+                "sha256:6295f6763749b89c994fcb6d8a7f7ce03c3992e695f89f00b741b4580b199b7e",
+                "sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa",
+                "sha256:718856856ba31f14f13ba885ff13874be7fefc53984d2832458f12c38205f7f7",
+                "sha256:7f7609a718b177bf171ac93cea9fd2ddc0e03e84d8fa4e887bdfc39671d46b00",
+                "sha256:80ca33961ced9c63358056bd08403ff866512038883e74f3a4bf88ad3eb66838",
+                "sha256:80fe64a6deb6fcfdf7b8386f2cf216d329be6f2781f7d90304351811fb591360",
+                "sha256:81c4b81611e3a3cb30e59b0cf05b888c675f97e3adb2c8672c3154047980726b",
+                "sha256:855c583f268edde09474b081e3ddcd5cf3b20c12f26e0d434e1386cc5d318e7a",
+                "sha256:9bfdb82cdfeccec50aad441afc332faf8606dfa5e8efd18a6692b5d6e79f00fd",
+                "sha256:a5d24e1d674dd9d72c66ad3ea9131322819ff86250b30dc5821cbafcfa0b96b4",
+                "sha256:a9f44cd7e162ac6191491d7249cceb02b8116b0f7e847ee33f739d7cb1ea1f70",
+                "sha256:b5b3f092fe345c03bca1e0b687dfbb39364b21ebb8ba90e3fa707374b7915204",
+                "sha256:b9618823bd237c0d2575283f2939655f54d51b4527ec3972907a927acbcc5bfc",
+                "sha256:cef9c85ccbe9bee00909758936ea841ef12035296c748aaceee535969e27d31b",
+                "sha256:d21237d0cd37acded35154e29aec853e945950321dd2ffd1a7d86fe686814669",
+                "sha256:d3c5c79ab7dfce6d88f1ba639b77e77a17ea33a01b07b99840d6ed08031cb2a7",
+                "sha256:d9d7942b624b04b895cb95af03a23407f17646815495ce4547f0e60e0b06f58e",
+                "sha256:db6d9fac65bd08cea7f3540b899977c6dee9edad959fa4eaf305940d9cbd861c",
+                "sha256:ede5af4a2702444a832a800b8eb7f0a7a1c0eed55b644642e049c98d589e5092",
+                "sha256:effb7749713d5317478bb3acb3f81d9d7c7f86726d41c1facca068a04cf5bb4c",
+                "sha256:f154d173286a5d1863637a7dcd8c3437bb557520b01bddb0be0258dcb72696b5",
+                "sha256:f25ed6e28ddf50de7e7ea99d7a976d6a9c415f03adcaac9c41ff6ff41b6d86ac"
+            ],
+            "index": "pypi",
+            "version": "==9.0.1"
+        },
+        "protobuf": {
+            "hashes": [
+                "sha256:072fbc78d705d3edc7ccac58a62c4c8e0cec856987da7df8aca86e647be4e35c",
+                "sha256:09297b7972da685ce269ec52af761743714996b4381c085205914c41fcab59fb",
+                "sha256:16f519de1313f1b7139ad70772e7db515b1420d208cb16c6d7858ea989fc64a9",
+                "sha256:1c91ef4110fdd2c590effb5dca8fdbdcb3bf563eece99287019c4204f53d81a4",
+                "sha256:3112b58aac3bac9c8be2b60a9daf6b558ca3f7681c130dcdd788ade7c9ffbdca",
+                "sha256:36cecbabbda242915529b8ff364f2263cd4de7c46bbe361418b5ed859677ba58",
+                "sha256:4276cdec4447bd5015453e41bdc0c0c1234eda08420b7c9a18b8d647add51e4b",
+                "sha256:435bb78b37fc386f9275a7035fe4fb1364484e38980d0dd91bc834a02c5ec909",
+                "sha256:48ed3877fa43e22bcacc852ca76d4775741f9709dd9575881a373bd3e85e54b2",
+                "sha256:54a1473077f3b616779ce31f477351a45b4fef8c9fd7892d6d87e287a38df368",
+                "sha256:69da7d39e39942bd52848438462674c463e23963a1fdaa84d88df7fbd7e749b2",
+                "sha256:6cbc312be5e71869d9d5ea25147cdf652a6781cf4d906497ca7690b7b9b5df13",
+                "sha256:7bb03bc2873a2842e5ebb4801f5c7ff1bfbdf426f85d0172f7644fcda0671ae0",
+                "sha256:7ca7da9c339ca8890d66958f5462beabd611eca6c958691a8fe6eccbd1eb0c6e",
+                "sha256:835a9c949dc193953c319603b2961c5c8f4327957fe23d914ca80d982665e8ee",
+                "sha256:84123274d982b9e248a143dadd1b9815049f4477dc783bf84efe6250eb4b836a",
+                "sha256:8961c3a78ebfcd000920c9060a262f082f29838682b1f7201889300c1fbe0616",
+                "sha256:96bd766831596d6014ca88d86dc8fe0fb2e428c0b02432fd9db3943202bf8c5e",
+                "sha256:9df0c10adf3e83015ced42a9a7bd64e13d06c4cf45c340d2c63020ea04499d0a",
+                "sha256:b38057450a0c566cbd04890a40edf916db890f2818e8682221611d78dc32ae26",
+                "sha256:bd95d1dfb9c4f4563e6093a9aa19d9c186bf98fa54da5252531cc0d3a07977e7",
+                "sha256:c1068287025f8ea025103e37d62ffd63fec8e9e636246b89c341aeda8a67c934",
+                "sha256:c438268eebb8cf039552897d78f402d734a404f1360592fef55297285f7f953f",
+                "sha256:cdc076c03381f5c1d9bb1abdcc5503d9ca8b53cf0a9d31a9f6754ec9e6c8af0f",
+                "sha256:f358aa33e03b7a84e0d91270a4d4d8f5df6921abe99a377828839e8ed0c04e07",
+                "sha256:f51d5a9f137f7a2cec2d326a74b6e3fc79d635d69ffe1b036d39fc7d75430d37"
+            ],
+            "version": "==3.19.4"
+        },
+        "pyasn1": {
+            "hashes": [
+                "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
+                "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
+                "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
+                "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
+                "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
+                "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
+                "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
+                "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
+                "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
+                "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
+                "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
+                "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
+                "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
+            ],
+            "version": "==0.4.8"
+        },
+        "pyasn1-modules": {
+            "hashes": [
+                "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
+                "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199",
+                "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
+                "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
+                "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4",
+                "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
+                "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74",
+                "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb",
+                "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
+                "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
+                "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
+                "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
+                "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"
+            ],
+            "version": "==0.2.8"
+        },
+        "pydantic": {
+            "hashes": [
+                "sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3",
+                "sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398",
+                "sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1",
+                "sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65",
+                "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4",
+                "sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16",
+                "sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2",
+                "sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c",
+                "sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6",
+                "sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce",
+                "sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9",
+                "sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3",
+                "sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034",
+                "sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c",
+                "sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a",
+                "sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77",
+                "sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b",
+                "sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6",
+                "sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f",
+                "sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721",
+                "sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37",
+                "sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032",
+                "sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d",
+                "sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed",
+                "sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6",
+                "sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054",
+                "sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25",
+                "sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46",
+                "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5",
+                "sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c",
+                "sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070",
+                "sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1",
+                "sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7",
+                "sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d",
+                "sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145"
+            ],
+            "index": "pypi",
+            "version": "==1.9.0"
+        },
+        "requests": {
+            "hashes": [
+                "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
+                "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
+            ],
+            "version": "==2.27.1"
+        },
+        "requests-oauthlib": {
+            "hashes": [
+                "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5",
+                "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"
+            ],
+            "version": "==1.3.1"
+        },
+        "rsa": {
+            "hashes": [
+                "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17",
+                "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==4.8"
+        },
+        "six": {
+            "hashes": [
+                "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
+                "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
+            ],
+            "version": "==1.16.0"
+        },
+        "starlette": {
+            "hashes": [
+                "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed",
+                "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"
+            ],
+            "version": "==0.14.2"
+        },
+        "tensorboard": {
+            "hashes": [
+                "sha256:65a338e4424e9079f2604923bdbe301792adce2ace1be68da6b3ddf005170def"
+            ],
+            "version": "==2.8.0"
+        },
+        "tensorboard-data-server": {
+            "hashes": [
+                "sha256:809fe9887682d35c1f7d1f54f0f40f98bb1f771b14265b453ca051e2ce58fca7",
+                "sha256:d8237580755e58eff68d1f3abefb5b1e39ae5c8b127cc40920f9c4fb33f4b98a",
+                "sha256:fa8cef9be4fcae2f2363c88176638baf2da19c5ec90addb49b1cde05c95c88ee"
+            ],
+            "version": "==0.6.1"
+        },
+        "tensorboard-plugin-wit": {
+            "hashes": [
+                "sha256:ff26bdd583d155aa951ee3b152b3d0cffae8005dc697f72b44a8e8c2a77a8cbe"
+            ],
+            "version": "==1.8.1"
+        },
+        "tensorflow": {
+            "hashes": [
+                "sha256:05fb161c6b2a6c4b8317a703a0a6d7f7aa6b5e3c6ea31bbc4f44ef96b89c3344",
+                "sha256:291fa84f1022914580810ad76732fb254e44a8a609128e1c58873a12b2f81559",
+                "sha256:2a520538e77a52fb428acb05e300c960844fd1d2c3918ca8ca14127edba6f83b",
+                "sha256:52f225fecc688281b3ae2cba2b52d3ed6215ed4a3ffb686b9cfd09885ca65563",
+                "sha256:78c3ba2e0c952aa9eb388200f1923e40287f9357492a464188ca3043e35edc52",
+                "sha256:8489b4f1771e146f752b0eaeb57acf183bd07357e4550464e7dff18b3b656b5d",
+                "sha256:9d91a989e5455ae713c03fd7236071ab3f232ad8ff2831f2658072933546091f",
+                "sha256:b360c13b3e58b9a5c0780cbdb6b549eea73f620275fa203f8508fe418ae02735",
+                "sha256:b7170844ae6b048d82a9d7a61b2fa627f2e16cb829267bf0ce4b3a0de0a61054",
+                "sha256:da38d4043185267e7316ae5dc98d18e89c8af4170859f64798e7a3607fd606e3",
+                "sha256:dd0f9f113ebc21b73fcd349db1629e187b8686395b8146d100eb1706a943bbc0",
+                "sha256:fa4a723368d5f748b6f4ec305cf7c26b98e4a6a8c2ce1425f8ae10383a37bcfc"
+            ],
+            "index": "pypi",
+            "version": "==2.8.0"
+        },
+        "tensorflow-io-gcs-filesystem": {
+            "hashes": [
+                "sha256:2862e0869453ce1f872a28d1362768ee078ec227ea587dd69164081dea6d7177",
+                "sha256:2f67d19a2f2579dc55f1590faf48c2e882cabb860992b5a9c7edb0ed8b3eb187",
+                "sha256:6e65009770a05a3b55c5f782348f785e5034d277a727832811ad737bd857c8c9",
+                "sha256:71c00638c9b6048480095f2738dfefd8f4b2e7b534190c91d699aee769bfa86e",
+                "sha256:825f396388748038ad38c35b091311982081f93a5db8ca9763fc874c3f555e6c",
+                "sha256:9c00f9a9880477b1dff0c71ee6734421ce99ac484ca2151793ebf2681fc0cb4c",
+                "sha256:aa90b9a34ea8da4dbd534f77746d67375714db869524da889193c3042352679a",
+                "sha256:b6ca3a9f751aa9c2f9851520e666d905ad14667281bbafeabe611b7b8f3e1bc5",
+                "sha256:cbc71b3925508bf796644a0083a6f9284f71404654f53092bece701383a69520",
+                "sha256:cc093f160f79526d31f6070a3ddc000868d737a36ccf40984128661563383601",
+                "sha256:cde835e68b2b43ddade07c999e7c3251bcd62b1ff165c34fbe9fc6e0f12c3ac9",
+                "sha256:d1eb5e9be62040c5a249ae8adaae7e61f65b59541139e4d6767157f25a224bf5"
+            ],
+            "version": "==0.24.0"
+        },
+        "termcolor": {
+            "hashes": [
+                "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"
+            ],
+            "version": "==1.1.0"
+        },
+        "tf-estimator-nightly": {
+            "hashes": [
+                "sha256:0065a04e396b2890bd19761fc1de7559ceafeba12839f8db2c7e7473afaaf612"
+            ],
+            "version": "==2.8.0.dev2021122109"
+        },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42",
+                "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"
+            ],
+            "version": "==4.1.1"
+        },
+        "urllib3": {
+            "hashes": [
+                "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
+                "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
+            ],
+            "version": "==1.26.8"
+        },
+        "uvicorn": {
+            "hashes": [
+                "sha256:17f898c64c71a2640514d4089da2689e5db1ce5d4086c2d53699bf99513421c1",
+                "sha256:d9a3c0dd1ca86728d3e235182683b4cf94cd53a867c288eaeca80ee781b2caff"
+            ],
+            "index": "pypi",
+            "version": "==0.15.0"
+        },
+        "werkzeug": {
+            "hashes": [
+                "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8",
+                "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"
+            ],
+            "version": "==2.0.3"
+        },
+        "wheel": {
+            "hashes": [
+                "sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a",
+                "sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4"
+            ],
+            "version": "==0.37.1"
+        },
+        "wrapt": {
+            "hashes": [
+                "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179",
+                "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096",
+                "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374",
+                "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df",
+                "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185",
+                "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785",
+                "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7",
+                "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909",
+                "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918",
+                "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33",
+                "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068",
+                "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829",
+                "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af",
+                "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79",
+                "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce",
+                "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc",
+                "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36",
+                "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade",
+                "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca",
+                "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32",
+                "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125",
+                "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e",
+                "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709",
+                "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f",
+                "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b",
+                "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb",
+                "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb",
+                "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489",
+                "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640",
+                "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb",
+                "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851",
+                "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d",
+                "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44",
+                "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13",
+                "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2",
+                "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb",
+                "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b",
+                "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9",
+                "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755",
+                "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c",
+                "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a",
+                "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf",
+                "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3",
+                "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229",
+                "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e",
+                "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de",
+                "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554",
+                "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10",
+                "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80",
+                "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056",
+                "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"
+            ],
+            "version": "==1.13.3"
+        },
+        "zipp": {
+            "hashes": [
+                "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d",
+                "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"
+            ],
+            "version": "==3.7.0"
+        }
+    },
+    "develop": {}
+}

+ 1002 - 0
machine_learning/app/imagenet_class_index.json

@@ -0,0 +1,1002 @@
+{
+  "0": ["n01440764", "tench"],
+  "1": ["n01443537", "goldfish"],
+  "2": ["n01484850", "great_white_shark"],
+  "3": ["n01491361", "tiger_shark"],
+  "4": ["n01494475", "hammerhead"],
+  "5": ["n01496331", "electric_ray"],
+  "6": ["n01498041", "stingray"],
+  "7": ["n01514668", "cock"],
+  "8": ["n01514859", "hen"],
+  "9": ["n01518878", "ostrich"],
+  "10": ["n01530575", "brambling"],
+  "11": ["n01531178", "goldfinch"],
+  "12": ["n01532829", "house_finch"],
+  "13": ["n01534433", "junco"],
+  "14": ["n01537544", "indigo_bunting"],
+  "15": ["n01558993", "robin"],
+  "16": ["n01560419", "bulbul"],
+  "17": ["n01580077", "jay"],
+  "18": ["n01582220", "magpie"],
+  "19": ["n01592084", "chickadee"],
+  "20": ["n01601694", "water_ouzel"],
+  "21": ["n01608432", "kite"],
+  "22": ["n01614925", "bald_eagle"],
+  "23": ["n01616318", "vulture"],
+  "24": ["n01622779", "great_grey_owl"],
+  "25": ["n01629819", "European_fire_salamander"],
+  "26": ["n01630670", "common_newt"],
+  "27": ["n01631663", "eft"],
+  "28": ["n01632458", "spotted_salamander"],
+  "29": ["n01632777", "axolotl"],
+  "30": ["n01641577", "bullfrog"],
+  "31": ["n01644373", "tree_frog"],
+  "32": ["n01644900", "tailed_frog"],
+  "33": ["n01664065", "loggerhead"],
+  "34": ["n01665541", "leatherback_turtle"],
+  "35": ["n01667114", "mud_turtle"],
+  "36": ["n01667778", "terrapin"],
+  "37": ["n01669191", "box_turtle"],
+  "38": ["n01675722", "banded_gecko"],
+  "39": ["n01677366", "common_iguana"],
+  "40": ["n01682714", "American_chameleon"],
+  "41": ["n01685808", "whiptail"],
+  "42": ["n01687978", "agama"],
+  "43": ["n01688243", "frilled_lizard"],
+  "44": ["n01689811", "alligator_lizard"],
+  "45": ["n01692333", "Gila_monster"],
+  "46": ["n01693334", "green_lizard"],
+  "47": ["n01694178", "African_chameleon"],
+  "48": ["n01695060", "Komodo_dragon"],
+  "49": ["n01697457", "African_crocodile"],
+  "50": ["n01698640", "American_alligator"],
+  "51": ["n01704323", "triceratops"],
+  "52": ["n01728572", "thunder_snake"],
+  "53": ["n01728920", "ringneck_snake"],
+  "54": ["n01729322", "hognose_snake"],
+  "55": ["n01729977", "green_snake"],
+  "56": ["n01734418", "king_snake"],
+  "57": ["n01735189", "garter_snake"],
+  "58": ["n01737021", "water_snake"],
+  "59": ["n01739381", "vine_snake"],
+  "60": ["n01740131", "night_snake"],
+  "61": ["n01742172", "boa_constrictor"],
+  "62": ["n01744401", "rock_python"],
+  "63": ["n01748264", "Indian_cobra"],
+  "64": ["n01749939", "green_mamba"],
+  "65": ["n01751748", "sea_snake"],
+  "66": ["n01753488", "horned_viper"],
+  "67": ["n01755581", "diamondback"],
+  "68": ["n01756291", "sidewinder"],
+  "69": ["n01768244", "trilobite"],
+  "70": ["n01770081", "harvestman"],
+  "71": ["n01770393", "scorpion"],
+  "72": ["n01773157", "black_and_gold_garden_spider"],
+  "73": ["n01773549", "barn_spider"],
+  "74": ["n01773797", "garden_spider"],
+  "75": ["n01774384", "black_widow"],
+  "76": ["n01774750", "tarantula"],
+  "77": ["n01775062", "wolf_spider"],
+  "78": ["n01776313", "tick"],
+  "79": ["n01784675", "centipede"],
+  "80": ["n01795545", "black_grouse"],
+  "81": ["n01796340", "ptarmigan"],
+  "82": ["n01797886", "ruffed_grouse"],
+  "83": ["n01798484", "prairie_chicken"],
+  "84": ["n01806143", "peacock"],
+  "85": ["n01806567", "quail"],
+  "86": ["n01807496", "partridge"],
+  "87": ["n01817953", "African_grey"],
+  "88": ["n01818515", "macaw"],
+  "89": ["n01819313", "sulphur-crested_cockatoo"],
+  "90": ["n01820546", "lorikeet"],
+  "91": ["n01824575", "coucal"],
+  "92": ["n01828970", "bee_eater"],
+  "93": ["n01829413", "hornbill"],
+  "94": ["n01833805", "hummingbird"],
+  "95": ["n01843065", "jacamar"],
+  "96": ["n01843383", "toucan"],
+  "97": ["n01847000", "drake"],
+  "98": ["n01855032", "red-breasted_merganser"],
+  "99": ["n01855672", "goose"],
+  "100": ["n01860187", "black_swan"],
+  "101": ["n01871265", "tusker"],
+  "102": ["n01872401", "echidna"],
+  "103": ["n01873310", "platypus"],
+  "104": ["n01877812", "wallaby"],
+  "105": ["n01882714", "koala"],
+  "106": ["n01883070", "wombat"],
+  "107": ["n01910747", "jellyfish"],
+  "108": ["n01914609", "sea_anemone"],
+  "109": ["n01917289", "brain_coral"],
+  "110": ["n01924916", "flatworm"],
+  "111": ["n01930112", "nematode"],
+  "112": ["n01943899", "conch"],
+  "113": ["n01944390", "snail"],
+  "114": ["n01945685", "slug"],
+  "115": ["n01950731", "sea_slug"],
+  "116": ["n01955084", "chiton"],
+  "117": ["n01968897", "chambered_nautilus"],
+  "118": ["n01978287", "Dungeness_crab"],
+  "119": ["n01978455", "rock_crab"],
+  "120": ["n01980166", "fiddler_crab"],
+  "121": ["n01981276", "king_crab"],
+  "122": ["n01983481", "American_lobster"],
+  "123": ["n01984695", "spiny_lobster"],
+  "124": ["n01985128", "crayfish"],
+  "125": ["n01986214", "hermit_crab"],
+  "126": ["n01990800", "isopod"],
+  "127": ["n02002556", "white_stork"],
+  "128": ["n02002724", "black_stork"],
+  "129": ["n02006656", "spoonbill"],
+  "130": ["n02007558", "flamingo"],
+  "131": ["n02009229", "little_blue_heron"],
+  "132": ["n02009912", "American_egret"],
+  "133": ["n02011460", "bittern"],
+  "134": ["n02012849", "crane"],
+  "135": ["n02013706", "limpkin"],
+  "136": ["n02017213", "European_gallinule"],
+  "137": ["n02018207", "American_coot"],
+  "138": ["n02018795", "bustard"],
+  "139": ["n02025239", "ruddy_turnstone"],
+  "140": ["n02027492", "red-backed_sandpiper"],
+  "141": ["n02028035", "redshank"],
+  "142": ["n02033041", "dowitcher"],
+  "143": ["n02037110", "oystercatcher"],
+  "144": ["n02051845", "pelican"],
+  "145": ["n02056570", "king_penguin"],
+  "146": ["n02058221", "albatross"],
+  "147": ["n02066245", "grey_whale"],
+  "148": ["n02071294", "killer_whale"],
+  "149": ["n02074367", "dugong"],
+  "150": ["n02077923", "sea_lion"],
+  "151": ["n02085620", "Chihuahua"],
+  "152": ["n02085782", "Japanese_spaniel"],
+  "153": ["n02085936", "Maltese_dog"],
+  "154": ["n02086079", "Pekinese"],
+  "155": ["n02086240", "Shih-Tzu"],
+  "156": ["n02086646", "Blenheim_spaniel"],
+  "157": ["n02086910", "papillon"],
+  "158": ["n02087046", "toy_terrier"],
+  "159": ["n02087394", "Rhodesian_ridgeback"],
+  "160": ["n02088094", "Afghan_hound"],
+  "161": ["n02088238", "basset"],
+  "162": ["n02088364", "beagle"],
+  "163": ["n02088466", "bloodhound"],
+  "164": ["n02088632", "bluetick"],
+  "165": ["n02089078", "black-and-tan_coonhound"],
+  "166": ["n02089867", "Walker_hound"],
+  "167": ["n02089973", "English_foxhound"],
+  "168": ["n02090379", "redbone"],
+  "169": ["n02090622", "borzoi"],
+  "170": ["n02090721", "Irish_wolfhound"],
+  "171": ["n02091032", "Italian_greyhound"],
+  "172": ["n02091134", "whippet"],
+  "173": ["n02091244", "Ibizan_hound"],
+  "174": ["n02091467", "Norwegian_elkhound"],
+  "175": ["n02091635", "otterhound"],
+  "176": ["n02091831", "Saluki"],
+  "177": ["n02092002", "Scottish_deerhound"],
+  "178": ["n02092339", "Weimaraner"],
+  "179": ["n02093256", "Staffordshire_bullterrier"],
+  "180": ["n02093428", "American_Staffordshire_terrier"],
+  "181": ["n02093647", "Bedlington_terrier"],
+  "182": ["n02093754", "Border_terrier"],
+  "183": ["n02093859", "Kerry_blue_terrier"],
+  "184": ["n02093991", "Irish_terrier"],
+  "185": ["n02094114", "Norfolk_terrier"],
+  "186": ["n02094258", "Norwich_terrier"],
+  "187": ["n02094433", "Yorkshire_terrier"],
+  "188": ["n02095314", "wire-haired_fox_terrier"],
+  "189": ["n02095570", "Lakeland_terrier"],
+  "190": ["n02095889", "Sealyham_terrier"],
+  "191": ["n02096051", "Airedale"],
+  "192": ["n02096177", "cairn"],
+  "193": ["n02096294", "Australian_terrier"],
+  "194": ["n02096437", "Dandie_Dinmont"],
+  "195": ["n02096585", "Boston_bull"],
+  "196": ["n02097047", "miniature_schnauzer"],
+  "197": ["n02097130", "giant_schnauzer"],
+  "198": ["n02097209", "standard_schnauzer"],
+  "199": ["n02097298", "Scotch_terrier"],
+  "200": ["n02097474", "Tibetan_terrier"],
+  "201": ["n02097658", "silky_terrier"],
+  "202": ["n02098105", "soft-coated_wheaten_terrier"],
+  "203": ["n02098286", "West_Highland_white_terrier"],
+  "204": ["n02098413", "Lhasa"],
+  "205": ["n02099267", "flat-coated_retriever"],
+  "206": ["n02099429", "curly-coated_retriever"],
+  "207": ["n02099601", "golden_retriever"],
+  "208": ["n02099712", "Labrador_retriever"],
+  "209": ["n02099849", "Chesapeake_Bay_retriever"],
+  "210": ["n02100236", "German_short-haired_pointer"],
+  "211": ["n02100583", "vizsla"],
+  "212": ["n02100735", "English_setter"],
+  "213": ["n02100877", "Irish_setter"],
+  "214": ["n02101006", "Gordon_setter"],
+  "215": ["n02101388", "Brittany_spaniel"],
+  "216": ["n02101556", "clumber"],
+  "217": ["n02102040", "English_springer"],
+  "218": ["n02102177", "Welsh_springer_spaniel"],
+  "219": ["n02102318", "cocker_spaniel"],
+  "220": ["n02102480", "Sussex_spaniel"],
+  "221": ["n02102973", "Irish_water_spaniel"],
+  "222": ["n02104029", "kuvasz"],
+  "223": ["n02104365", "schipperke"],
+  "224": ["n02105056", "groenendael"],
+  "225": ["n02105162", "malinois"],
+  "226": ["n02105251", "briard"],
+  "227": ["n02105412", "kelpie"],
+  "228": ["n02105505", "komondor"],
+  "229": ["n02105641", "Old_English_sheepdog"],
+  "230": ["n02105855", "Shetland_sheepdog"],
+  "231": ["n02106030", "collie"],
+  "232": ["n02106166", "Border_collie"],
+  "233": ["n02106382", "Bouvier_des_Flandres"],
+  "234": ["n02106550", "Rottweiler"],
+  "235": ["n02106662", "German_shepherd"],
+  "236": ["n02107142", "Doberman"],
+  "237": ["n02107312", "miniature_pinscher"],
+  "238": ["n02107574", "Greater_Swiss_Mountain_dog"],
+  "239": ["n02107683", "Bernese_mountain_dog"],
+  "240": ["n02107908", "Appenzeller"],
+  "241": ["n02108000", "EntleBucher"],
+  "242": ["n02108089", "boxer"],
+  "243": ["n02108422", "bull_mastiff"],
+  "244": ["n02108551", "Tibetan_mastiff"],
+  "245": ["n02108915", "French_bulldog"],
+  "246": ["n02109047", "Great_Dane"],
+  "247": ["n02109525", "Saint_Bernard"],
+  "248": ["n02109961", "Eskimo_dog"],
+  "249": ["n02110063", "malamute"],
+  "250": ["n02110185", "Siberian_husky"],
+  "251": ["n02110341", "dalmatian"],
+  "252": ["n02110627", "affenpinscher"],
+  "253": ["n02110806", "basenji"],
+  "254": ["n02110958", "pug"],
+  "255": ["n02111129", "Leonberg"],
+  "256": ["n02111277", "Newfoundland"],
+  "257": ["n02111500", "Great_Pyrenees"],
+  "258": ["n02111889", "Samoyed"],
+  "259": ["n02112018", "Pomeranian"],
+  "260": ["n02112137", "chow"],
+  "261": ["n02112350", "keeshond"],
+  "262": ["n02112706", "Brabancon_griffon"],
+  "263": ["n02113023", "Pembroke"],
+  "264": ["n02113186", "Cardigan"],
+  "265": ["n02113624", "toy_poodle"],
+  "266": ["n02113712", "miniature_poodle"],
+  "267": ["n02113799", "standard_poodle"],
+  "268": ["n02113978", "Mexican_hairless"],
+  "269": ["n02114367", "timber_wolf"],
+  "270": ["n02114548", "white_wolf"],
+  "271": ["n02114712", "red_wolf"],
+  "272": ["n02114855", "coyote"],
+  "273": ["n02115641", "dingo"],
+  "274": ["n02115913", "dhole"],
+  "275": ["n02116738", "African_hunting_dog"],
+  "276": ["n02117135", "hyena"],
+  "277": ["n02119022", "red_fox"],
+  "278": ["n02119789", "kit_fox"],
+  "279": ["n02120079", "Arctic_fox"],
+  "280": ["n02120505", "grey_fox"],
+  "281": ["n02123045", "tabby"],
+  "282": ["n02123159", "tiger_cat"],
+  "283": ["n02123394", "Persian_cat"],
+  "284": ["n02123597", "Siamese_cat"],
+  "285": ["n02124075", "Egyptian_cat"],
+  "286": ["n02125311", "cougar"],
+  "287": ["n02127052", "lynx"],
+  "288": ["n02128385", "leopard"],
+  "289": ["n02128757", "snow_leopard"],
+  "290": ["n02128925", "jaguar"],
+  "291": ["n02129165", "lion"],
+  "292": ["n02129604", "tiger"],
+  "293": ["n02130308", "cheetah"],
+  "294": ["n02132136", "brown_bear"],
+  "295": ["n02133161", "American_black_bear"],
+  "296": ["n02134084", "ice_bear"],
+  "297": ["n02134418", "sloth_bear"],
+  "298": ["n02137549", "mongoose"],
+  "299": ["n02138441", "meerkat"],
+  "300": ["n02165105", "tiger_beetle"],
+  "301": ["n02165456", "ladybug"],
+  "302": ["n02167151", "ground_beetle"],
+  "303": ["n02168699", "long-horned_beetle"],
+  "304": ["n02169497", "leaf_beetle"],
+  "305": ["n02172182", "dung_beetle"],
+  "306": ["n02174001", "rhinoceros_beetle"],
+  "307": ["n02177972", "weevil"],
+  "308": ["n02190166", "fly"],
+  "309": ["n02206856", "bee"],
+  "310": ["n02219486", "ant"],
+  "311": ["n02226429", "grasshopper"],
+  "312": ["n02229544", "cricket"],
+  "313": ["n02231487", "walking_stick"],
+  "314": ["n02233338", "cockroach"],
+  "315": ["n02236044", "mantis"],
+  "316": ["n02256656", "cicada"],
+  "317": ["n02259212", "leafhopper"],
+  "318": ["n02264363", "lacewing"],
+  "319": ["n02268443", "dragonfly"],
+  "320": ["n02268853", "damselfly"],
+  "321": ["n02276258", "admiral"],
+  "322": ["n02277742", "ringlet"],
+  "323": ["n02279972", "monarch"],
+  "324": ["n02280649", "cabbage_butterfly"],
+  "325": ["n02281406", "sulphur_butterfly"],
+  "326": ["n02281787", "lycaenid"],
+  "327": ["n02317335", "starfish"],
+  "328": ["n02319095", "sea_urchin"],
+  "329": ["n02321529", "sea_cucumber"],
+  "330": ["n02325366", "wood_rabbit"],
+  "331": ["n02326432", "hare"],
+  "332": ["n02328150", "Angora"],
+  "333": ["n02342885", "hamster"],
+  "334": ["n02346627", "porcupine"],
+  "335": ["n02356798", "fox_squirrel"],
+  "336": ["n02361337", "marmot"],
+  "337": ["n02363005", "beaver"],
+  "338": ["n02364673", "guinea_pig"],
+  "339": ["n02389026", "sorrel"],
+  "340": ["n02391049", "zebra"],
+  "341": ["n02395406", "hog"],
+  "342": ["n02396427", "wild_boar"],
+  "343": ["n02397096", "warthog"],
+  "344": ["n02398521", "hippopotamus"],
+  "345": ["n02403003", "ox"],
+  "346": ["n02408429", "water_buffalo"],
+  "347": ["n02410509", "bison"],
+  "348": ["n02412080", "ram"],
+  "349": ["n02415577", "bighorn"],
+  "350": ["n02417914", "ibex"],
+  "351": ["n02422106", "hartebeest"],
+  "352": ["n02422699", "impala"],
+  "353": ["n02423022", "gazelle"],
+  "354": ["n02437312", "Arabian_camel"],
+  "355": ["n02437616", "llama"],
+  "356": ["n02441942", "weasel"],
+  "357": ["n02442845", "mink"],
+  "358": ["n02443114", "polecat"],
+  "359": ["n02443484", "black-footed_ferret"],
+  "360": ["n02444819", "otter"],
+  "361": ["n02445715", "skunk"],
+  "362": ["n02447366", "badger"],
+  "363": ["n02454379", "armadillo"],
+  "364": ["n02457408", "three-toed_sloth"],
+  "365": ["n02480495", "orangutan"],
+  "366": ["n02480855", "gorilla"],
+  "367": ["n02481823", "chimpanzee"],
+  "368": ["n02483362", "gibbon"],
+  "369": ["n02483708", "siamang"],
+  "370": ["n02484975", "guenon"],
+  "371": ["n02486261", "patas"],
+  "372": ["n02486410", "baboon"],
+  "373": ["n02487347", "macaque"],
+  "374": ["n02488291", "langur"],
+  "375": ["n02488702", "colobus"],
+  "376": ["n02489166", "proboscis_monkey"],
+  "377": ["n02490219", "marmoset"],
+  "378": ["n02492035", "capuchin"],
+  "379": ["n02492660", "howler_monkey"],
+  "380": ["n02493509", "titi"],
+  "381": ["n02493793", "spider_monkey"],
+  "382": ["n02494079", "squirrel_monkey"],
+  "383": ["n02497673", "Madagascar_cat"],
+  "384": ["n02500267", "indri"],
+  "385": ["n02504013", "Indian_elephant"],
+  "386": ["n02504458", "African_elephant"],
+  "387": ["n02509815", "lesser_panda"],
+  "388": ["n02510455", "giant_panda"],
+  "389": ["n02514041", "barracouta"],
+  "390": ["n02526121", "eel"],
+  "391": ["n02536864", "coho"],
+  "392": ["n02606052", "rock_beauty"],
+  "393": ["n02607072", "anemone_fish"],
+  "394": ["n02640242", "sturgeon"],
+  "395": ["n02641379", "gar"],
+  "396": ["n02643566", "lionfish"],
+  "397": ["n02655020", "puffer"],
+  "398": ["n02666196", "abacus"],
+  "399": ["n02667093", "abaya"],
+  "400": ["n02669723", "academic_gown"],
+  "401": ["n02672831", "accordion"],
+  "402": ["n02676566", "acoustic_guitar"],
+  "403": ["n02687172", "aircraft_carrier"],
+  "404": ["n02690373", "airliner"],
+  "405": ["n02692877", "airship"],
+  "406": ["n02699494", "altar"],
+  "407": ["n02701002", "ambulance"],
+  "408": ["n02704792", "amphibian"],
+  "409": ["n02708093", "analog_clock"],
+  "410": ["n02727426", "apiary"],
+  "411": ["n02730930", "apron"],
+  "412": ["n02747177", "ashcan"],
+  "413": ["n02749479", "assault_rifle"],
+  "414": ["n02769748", "backpack"],
+  "415": ["n02776631", "bakery"],
+  "416": ["n02777292", "balance_beam"],
+  "417": ["n02782093", "balloon"],
+  "418": ["n02783161", "ballpoint"],
+  "419": ["n02786058", "Band_Aid"],
+  "420": ["n02787622", "banjo"],
+  "421": ["n02788148", "bannister"],
+  "422": ["n02790996", "barbell"],
+  "423": ["n02791124", "barber_chair"],
+  "424": ["n02791270", "barbershop"],
+  "425": ["n02793495", "barn"],
+  "426": ["n02794156", "barometer"],
+  "427": ["n02795169", "barrel"],
+  "428": ["n02797295", "barrow"],
+  "429": ["n02799071", "baseball"],
+  "430": ["n02802426", "basketball"],
+  "431": ["n02804414", "bassinet"],
+  "432": ["n02804610", "bassoon"],
+  "433": ["n02807133", "bathing_cap"],
+  "434": ["n02808304", "bath_towel"],
+  "435": ["n02808440", "bathtub"],
+  "436": ["n02814533", "beach_wagon"],
+  "437": ["n02814860", "beacon"],
+  "438": ["n02815834", "beaker"],
+  "439": ["n02817516", "bearskin"],
+  "440": ["n02823428", "beer_bottle"],
+  "441": ["n02823750", "beer_glass"],
+  "442": ["n02825657", "bell_cote"],
+  "443": ["n02834397", "bib"],
+  "444": ["n02835271", "bicycle-built-for-two"],
+  "445": ["n02837789", "bikini"],
+  "446": ["n02840245", "binder"],
+  "447": ["n02841315", "binoculars"],
+  "448": ["n02843684", "birdhouse"],
+  "449": ["n02859443", "boathouse"],
+  "450": ["n02860847", "bobsled"],
+  "451": ["n02865351", "bolo_tie"],
+  "452": ["n02869837", "bonnet"],
+  "453": ["n02870880", "bookcase"],
+  "454": ["n02871525", "bookshop"],
+  "455": ["n02877765", "bottlecap"],
+  "456": ["n02879718", "bow"],
+  "457": ["n02883205", "bow_tie"],
+  "458": ["n02892201", "brass"],
+  "459": ["n02892767", "brassiere"],
+  "460": ["n02894605", "breakwater"],
+  "461": ["n02895154", "breastplate"],
+  "462": ["n02906734", "broom"],
+  "463": ["n02909870", "bucket"],
+  "464": ["n02910353", "buckle"],
+  "465": ["n02916936", "bulletproof_vest"],
+  "466": ["n02917067", "bullet_train"],
+  "467": ["n02927161", "butcher_shop"],
+  "468": ["n02930766", "cab"],
+  "469": ["n02939185", "caldron"],
+  "470": ["n02948072", "candle"],
+  "471": ["n02950826", "cannon"],
+  "472": ["n02951358", "canoe"],
+  "473": ["n02951585", "can_opener"],
+  "474": ["n02963159", "cardigan"],
+  "475": ["n02965783", "car_mirror"],
+  "476": ["n02966193", "carousel"],
+  "477": ["n02966687", "carpenter's_kit"],
+  "478": ["n02971356", "carton"],
+  "479": ["n02974003", "car_wheel"],
+  "480": ["n02977058", "cash_machine"],
+  "481": ["n02978881", "cassette"],
+  "482": ["n02979186", "cassette_player"],
+  "483": ["n02980441", "castle"],
+  "484": ["n02981792", "catamaran"],
+  "485": ["n02988304", "CD_player"],
+  "486": ["n02992211", "cello"],
+  "487": ["n02992529", "cellular_telephone"],
+  "488": ["n02999410", "chain"],
+  "489": ["n03000134", "chainlink_fence"],
+  "490": ["n03000247", "chain_mail"],
+  "491": ["n03000684", "chain_saw"],
+  "492": ["n03014705", "chest"],
+  "493": ["n03016953", "chiffonier"],
+  "494": ["n03017168", "chime"],
+  "495": ["n03018349", "china_cabinet"],
+  "496": ["n03026506", "Christmas_stocking"],
+  "497": ["n03028079", "church"],
+  "498": ["n03032252", "cinema"],
+  "499": ["n03041632", "cleaver"],
+  "500": ["n03042490", "cliff_dwelling"],
+  "501": ["n03045698", "cloak"],
+  "502": ["n03047690", "clog"],
+  "503": ["n03062245", "cocktail_shaker"],
+  "504": ["n03063599", "coffee_mug"],
+  "505": ["n03063689", "coffeepot"],
+  "506": ["n03065424", "coil"],
+  "507": ["n03075370", "combination_lock"],
+  "508": ["n03085013", "computer_keyboard"],
+  "509": ["n03089624", "confectionery"],
+  "510": ["n03095699", "container_ship"],
+  "511": ["n03100240", "convertible"],
+  "512": ["n03109150", "corkscrew"],
+  "513": ["n03110669", "cornet"],
+  "514": ["n03124043", "cowboy_boot"],
+  "515": ["n03124170", "cowboy_hat"],
+  "516": ["n03125729", "cradle"],
+  "517": ["n03126707", "crane"],
+  "518": ["n03127747", "crash_helmet"],
+  "519": ["n03127925", "crate"],
+  "520": ["n03131574", "crib"],
+  "521": ["n03133878", "Crock_Pot"],
+  "522": ["n03134739", "croquet_ball"],
+  "523": ["n03141823", "crutch"],
+  "524": ["n03146219", "cuirass"],
+  "525": ["n03160309", "dam"],
+  "526": ["n03179701", "desk"],
+  "527": ["n03180011", "desktop_computer"],
+  "528": ["n03187595", "dial_telephone"],
+  "529": ["n03188531", "diaper"],
+  "530": ["n03196217", "digital_clock"],
+  "531": ["n03197337", "digital_watch"],
+  "532": ["n03201208", "dining_table"],
+  "533": ["n03207743", "dishrag"],
+  "534": ["n03207941", "dishwasher"],
+  "535": ["n03208938", "disk_brake"],
+  "536": ["n03216828", "dock"],
+  "537": ["n03218198", "dogsled"],
+  "538": ["n03220513", "dome"],
+  "539": ["n03223299", "doormat"],
+  "540": ["n03240683", "drilling_platform"],
+  "541": ["n03249569", "drum"],
+  "542": ["n03250847", "drumstick"],
+  "543": ["n03255030", "dumbbell"],
+  "544": ["n03259280", "Dutch_oven"],
+  "545": ["n03271574", "electric_fan"],
+  "546": ["n03272010", "electric_guitar"],
+  "547": ["n03272562", "electric_locomotive"],
+  "548": ["n03290653", "entertainment_center"],
+  "549": ["n03291819", "envelope"],
+  "550": ["n03297495", "espresso_maker"],
+  "551": ["n03314780", "face_powder"],
+  "552": ["n03325584", "feather_boa"],
+  "553": ["n03337140", "file"],
+  "554": ["n03344393", "fireboat"],
+  "555": ["n03345487", "fire_engine"],
+  "556": ["n03347037", "fire_screen"],
+  "557": ["n03355925", "flagpole"],
+  "558": ["n03372029", "flute"],
+  "559": ["n03376595", "folding_chair"],
+  "560": ["n03379051", "football_helmet"],
+  "561": ["n03384352", "forklift"],
+  "562": ["n03388043", "fountain"],
+  "563": ["n03388183", "fountain_pen"],
+  "564": ["n03388549", "four-poster"],
+  "565": ["n03393912", "freight_car"],
+  "566": ["n03394916", "French_horn"],
+  "567": ["n03400231", "frying_pan"],
+  "568": ["n03404251", "fur_coat"],
+  "569": ["n03417042", "garbage_truck"],
+  "570": ["n03424325", "gasmask"],
+  "571": ["n03425413", "gas_pump"],
+  "572": ["n03443371", "goblet"],
+  "573": ["n03444034", "go-kart"],
+  "574": ["n03445777", "golf_ball"],
+  "575": ["n03445924", "golfcart"],
+  "576": ["n03447447", "gondola"],
+  "577": ["n03447721", "gong"],
+  "578": ["n03450230", "gown"],
+  "579": ["n03452741", "grand_piano"],
+  "580": ["n03457902", "greenhouse"],
+  "581": ["n03459775", "grille"],
+  "582": ["n03461385", "grocery_store"],
+  "583": ["n03467068", "guillotine"],
+  "584": ["n03476684", "hair_slide"],
+  "585": ["n03476991", "hair_spray"],
+  "586": ["n03478589", "half_track"],
+  "587": ["n03481172", "hammer"],
+  "588": ["n03482405", "hamper"],
+  "589": ["n03483316", "hand_blower"],
+  "590": ["n03485407", "hand-held_computer"],
+  "591": ["n03485794", "handkerchief"],
+  "592": ["n03492542", "hard_disc"],
+  "593": ["n03494278", "harmonica"],
+  "594": ["n03495258", "harp"],
+  "595": ["n03496892", "harvester"],
+  "596": ["n03498962", "hatchet"],
+  "597": ["n03527444", "holster"],
+  "598": ["n03529860", "home_theater"],
+  "599": ["n03530642", "honeycomb"],
+  "600": ["n03532672", "hook"],
+  "601": ["n03534580", "hoopskirt"],
+  "602": ["n03535780", "horizontal_bar"],
+  "603": ["n03538406", "horse_cart"],
+  "604": ["n03544143", "hourglass"],
+  "605": ["n03584254", "iPod"],
+  "606": ["n03584829", "iron"],
+  "607": ["n03590841", "jack-o'-lantern"],
+  "608": ["n03594734", "jean"],
+  "609": ["n03594945", "jeep"],
+  "610": ["n03595614", "jersey"],
+  "611": ["n03598930", "jigsaw_puzzle"],
+  "612": ["n03599486", "jinrikisha"],
+  "613": ["n03602883", "joystick"],
+  "614": ["n03617480", "kimono"],
+  "615": ["n03623198", "knee_pad"],
+  "616": ["n03627232", "knot"],
+  "617": ["n03630383", "lab_coat"],
+  "618": ["n03633091", "ladle"],
+  "619": ["n03637318", "lampshade"],
+  "620": ["n03642806", "laptop"],
+  "621": ["n03649909", "lawn_mower"],
+  "622": ["n03657121", "lens_cap"],
+  "623": ["n03658185", "letter_opener"],
+  "624": ["n03661043", "library"],
+  "625": ["n03662601", "lifeboat"],
+  "626": ["n03666591", "lighter"],
+  "627": ["n03670208", "limousine"],
+  "628": ["n03673027", "liner"],
+  "629": ["n03676483", "lipstick"],
+  "630": ["n03680355", "Loafer"],
+  "631": ["n03690938", "lotion"],
+  "632": ["n03691459", "loudspeaker"],
+  "633": ["n03692522", "loupe"],
+  "634": ["n03697007", "lumbermill"],
+  "635": ["n03706229", "magnetic_compass"],
+  "636": ["n03709823", "mailbag"],
+  "637": ["n03710193", "mailbox"],
+  "638": ["n03710637", "maillot"],
+  "639": ["n03710721", "maillot"],
+  "640": ["n03717622", "manhole_cover"],
+  "641": ["n03720891", "maraca"],
+  "642": ["n03721384", "marimba"],
+  "643": ["n03724870", "mask"],
+  "644": ["n03729826", "matchstick"],
+  "645": ["n03733131", "maypole"],
+  "646": ["n03733281", "maze"],
+  "647": ["n03733805", "measuring_cup"],
+  "648": ["n03742115", "medicine_chest"],
+  "649": ["n03743016", "megalith"],
+  "650": ["n03759954", "microphone"],
+  "651": ["n03761084", "microwave"],
+  "652": ["n03763968", "military_uniform"],
+  "653": ["n03764736", "milk_can"],
+  "654": ["n03769881", "minibus"],
+  "655": ["n03770439", "miniskirt"],
+  "656": ["n03770679", "minivan"],
+  "657": ["n03773504", "missile"],
+  "658": ["n03775071", "mitten"],
+  "659": ["n03775546", "mixing_bowl"],
+  "660": ["n03776460", "mobile_home"],
+  "661": ["n03777568", "Model_T"],
+  "662": ["n03777754", "modem"],
+  "663": ["n03781244", "monastery"],
+  "664": ["n03782006", "monitor"],
+  "665": ["n03785016", "moped"],
+  "666": ["n03786901", "mortar"],
+  "667": ["n03787032", "mortarboard"],
+  "668": ["n03788195", "mosque"],
+  "669": ["n03788365", "mosquito_net"],
+  "670": ["n03791053", "motor_scooter"],
+  "671": ["n03792782", "mountain_bike"],
+  "672": ["n03792972", "mountain_tent"],
+  "673": ["n03793489", "mouse"],
+  "674": ["n03794056", "mousetrap"],
+  "675": ["n03796401", "moving_van"],
+  "676": ["n03803284", "muzzle"],
+  "677": ["n03804744", "nail"],
+  "678": ["n03814639", "neck_brace"],
+  "679": ["n03814906", "necklace"],
+  "680": ["n03825788", "nipple"],
+  "681": ["n03832673", "notebook"],
+  "682": ["n03837869", "obelisk"],
+  "683": ["n03838899", "oboe"],
+  "684": ["n03840681", "ocarina"],
+  "685": ["n03841143", "odometer"],
+  "686": ["n03843555", "oil_filter"],
+  "687": ["n03854065", "organ"],
+  "688": ["n03857828", "oscilloscope"],
+  "689": ["n03866082", "overskirt"],
+  "690": ["n03868242", "oxcart"],
+  "691": ["n03868863", "oxygen_mask"],
+  "692": ["n03871628", "packet"],
+  "693": ["n03873416", "paddle"],
+  "694": ["n03874293", "paddlewheel"],
+  "695": ["n03874599", "padlock"],
+  "696": ["n03876231", "paintbrush"],
+  "697": ["n03877472", "pajama"],
+  "698": ["n03877845", "palace"],
+  "699": ["n03884397", "panpipe"],
+  "700": ["n03887697", "paper_towel"],
+  "701": ["n03888257", "parachute"],
+  "702": ["n03888605", "parallel_bars"],
+  "703": ["n03891251", "park_bench"],
+  "704": ["n03891332", "parking_meter"],
+  "705": ["n03895866", "passenger_car"],
+  "706": ["n03899768", "patio"],
+  "707": ["n03902125", "pay-phone"],
+  "708": ["n03903868", "pedestal"],
+  "709": ["n03908618", "pencil_box"],
+  "710": ["n03908714", "pencil_sharpener"],
+  "711": ["n03916031", "perfume"],
+  "712": ["n03920288", "Petri_dish"],
+  "713": ["n03924679", "photocopier"],
+  "714": ["n03929660", "pick"],
+  "715": ["n03929855", "pickelhaube"],
+  "716": ["n03930313", "picket_fence"],
+  "717": ["n03930630", "pickup"],
+  "718": ["n03933933", "pier"],
+  "719": ["n03935335", "piggy_bank"],
+  "720": ["n03937543", "pill_bottle"],
+  "721": ["n03938244", "pillow"],
+  "722": ["n03942813", "ping-pong_ball"],
+  "723": ["n03944341", "pinwheel"],
+  "724": ["n03947888", "pirate"],
+  "725": ["n03950228", "pitcher"],
+  "726": ["n03954731", "plane"],
+  "727": ["n03956157", "planetarium"],
+  "728": ["n03958227", "plastic_bag"],
+  "729": ["n03961711", "plate_rack"],
+  "730": ["n03967562", "plow"],
+  "731": ["n03970156", "plunger"],
+  "732": ["n03976467", "Polaroid_camera"],
+  "733": ["n03976657", "pole"],
+  "734": ["n03977966", "police_van"],
+  "735": ["n03980874", "poncho"],
+  "736": ["n03982430", "pool_table"],
+  "737": ["n03983396", "pop_bottle"],
+  "738": ["n03991062", "pot"],
+  "739": ["n03992509", "potter's_wheel"],
+  "740": ["n03995372", "power_drill"],
+  "741": ["n03998194", "prayer_rug"],
+  "742": ["n04004767", "printer"],
+  "743": ["n04005630", "prison"],
+  "744": ["n04008634", "projectile"],
+  "745": ["n04009552", "projector"],
+  "746": ["n04019541", "puck"],
+  "747": ["n04023962", "punching_bag"],
+  "748": ["n04026417", "purse"],
+  "749": ["n04033901", "quill"],
+  "750": ["n04033995", "quilt"],
+  "751": ["n04037443", "racer"],
+  "752": ["n04039381", "racket"],
+  "753": ["n04040759", "radiator"],
+  "754": ["n04041544", "radio"],
+  "755": ["n04044716", "radio_telescope"],
+  "756": ["n04049303", "rain_barrel"],
+  "757": ["n04065272", "recreational_vehicle"],
+  "758": ["n04067472", "reel"],
+  "759": ["n04069434", "reflex_camera"],
+  "760": ["n04070727", "refrigerator"],
+  "761": ["n04074963", "remote_control"],
+  "762": ["n04081281", "restaurant"],
+  "763": ["n04086273", "revolver"],
+  "764": ["n04090263", "rifle"],
+  "765": ["n04099969", "rocking_chair"],
+  "766": ["n04111531", "rotisserie"],
+  "767": ["n04116512", "rubber_eraser"],
+  "768": ["n04118538", "rugby_ball"],
+  "769": ["n04118776", "rule"],
+  "770": ["n04120489", "running_shoe"],
+  "771": ["n04125021", "safe"],
+  "772": ["n04127249", "safety_pin"],
+  "773": ["n04131690", "saltshaker"],
+  "774": ["n04133789", "sandal"],
+  "775": ["n04136333", "sarong"],
+  "776": ["n04141076", "sax"],
+  "777": ["n04141327", "scabbard"],
+  "778": ["n04141975", "scale"],
+  "779": ["n04146614", "school_bus"],
+  "780": ["n04147183", "schooner"],
+  "781": ["n04149813", "scoreboard"],
+  "782": ["n04152593", "screen"],
+  "783": ["n04153751", "screw"],
+  "784": ["n04154565", "screwdriver"],
+  "785": ["n04162706", "seat_belt"],
+  "786": ["n04179913", "sewing_machine"],
+  "787": ["n04192698", "shield"],
+  "788": ["n04200800", "shoe_shop"],
+  "789": ["n04201297", "shoji"],
+  "790": ["n04204238", "shopping_basket"],
+  "791": ["n04204347", "shopping_cart"],
+  "792": ["n04208210", "shovel"],
+  "793": ["n04209133", "shower_cap"],
+  "794": ["n04209239", "shower_curtain"],
+  "795": ["n04228054", "ski"],
+  "796": ["n04229816", "ski_mask"],
+  "797": ["n04235860", "sleeping_bag"],
+  "798": ["n04238763", "slide_rule"],
+  "799": ["n04239074", "sliding_door"],
+  "800": ["n04243546", "slot"],
+  "801": ["n04251144", "snorkel"],
+  "802": ["n04252077", "snowmobile"],
+  "803": ["n04252225", "snowplow"],
+  "804": ["n04254120", "soap_dispenser"],
+  "805": ["n04254680", "soccer_ball"],
+  "806": ["n04254777", "sock"],
+  "807": ["n04258138", "solar_dish"],
+  "808": ["n04259630", "sombrero"],
+  "809": ["n04263257", "soup_bowl"],
+  "810": ["n04264628", "space_bar"],
+  "811": ["n04265275", "space_heater"],
+  "812": ["n04266014", "space_shuttle"],
+  "813": ["n04270147", "spatula"],
+  "814": ["n04273569", "speedboat"],
+  "815": ["n04275548", "spider_web"],
+  "816": ["n04277352", "spindle"],
+  "817": ["n04285008", "sports_car"],
+  "818": ["n04286575", "spotlight"],
+  "819": ["n04296562", "stage"],
+  "820": ["n04310018", "steam_locomotive"],
+  "821": ["n04311004", "steel_arch_bridge"],
+  "822": ["n04311174", "steel_drum"],
+  "823": ["n04317175", "stethoscope"],
+  "824": ["n04325704", "stole"],
+  "825": ["n04326547", "stone_wall"],
+  "826": ["n04328186", "stopwatch"],
+  "827": ["n04330267", "stove"],
+  "828": ["n04332243", "strainer"],
+  "829": ["n04335435", "streetcar"],
+  "830": ["n04336792", "stretcher"],
+  "831": ["n04344873", "studio_couch"],
+  "832": ["n04346328", "stupa"],
+  "833": ["n04347754", "submarine"],
+  "834": ["n04350905", "suit"],
+  "835": ["n04355338", "sundial"],
+  "836": ["n04355933", "sunglass"],
+  "837": ["n04356056", "sunglasses"],
+  "838": ["n04357314", "sunscreen"],
+  "839": ["n04366367", "suspension_bridge"],
+  "840": ["n04367480", "swab"],
+  "841": ["n04370456", "sweatshirt"],
+  "842": ["n04371430", "swimming_trunks"],
+  "843": ["n04371774", "swing"],
+  "844": ["n04372370", "switch"],
+  "845": ["n04376876", "syringe"],
+  "846": ["n04380533", "table_lamp"],
+  "847": ["n04389033", "tank"],
+  "848": ["n04392985", "tape_player"],
+  "849": ["n04398044", "teapot"],
+  "850": ["n04399382", "teddy"],
+  "851": ["n04404412", "television"],
+  "852": ["n04409515", "tennis_ball"],
+  "853": ["n04417672", "thatch"],
+  "854": ["n04418357", "theater_curtain"],
+  "855": ["n04423845", "thimble"],
+  "856": ["n04428191", "thresher"],
+  "857": ["n04429376", "throne"],
+  "858": ["n04435653", "tile_roof"],
+  "859": ["n04442312", "toaster"],
+  "860": ["n04443257", "tobacco_shop"],
+  "861": ["n04447861", "toilet_seat"],
+  "862": ["n04456115", "torch"],
+  "863": ["n04458633", "totem_pole"],
+  "864": ["n04461696", "tow_truck"],
+  "865": ["n04462240", "toyshop"],
+  "866": ["n04465501", "tractor"],
+  "867": ["n04467665", "trailer_truck"],
+  "868": ["n04476259", "tray"],
+  "869": ["n04479046", "trench_coat"],
+  "870": ["n04482393", "tricycle"],
+  "871": ["n04483307", "trimaran"],
+  "872": ["n04485082", "tripod"],
+  "873": ["n04486054", "triumphal_arch"],
+  "874": ["n04487081", "trolleybus"],
+  "875": ["n04487394", "trombone"],
+  "876": ["n04493381", "tub"],
+  "877": ["n04501370", "turnstile"],
+  "878": ["n04505470", "typewriter_keyboard"],
+  "879": ["n04507155", "umbrella"],
+  "880": ["n04509417", "unicycle"],
+  "881": ["n04515003", "upright"],
+  "882": ["n04517823", "vacuum"],
+  "883": ["n04522168", "vase"],
+  "884": ["n04523525", "vault"],
+  "885": ["n04525038", "velvet"],
+  "886": ["n04525305", "vending_machine"],
+  "887": ["n04532106", "vestment"],
+  "888": ["n04532670", "viaduct"],
+  "889": ["n04536866", "violin"],
+  "890": ["n04540053", "volleyball"],
+  "891": ["n04542943", "waffle_iron"],
+  "892": ["n04548280", "wall_clock"],
+  "893": ["n04548362", "wallet"],
+  "894": ["n04550184", "wardrobe"],
+  "895": ["n04552348", "warplane"],
+  "896": ["n04553703", "washbasin"],
+  "897": ["n04554684", "washer"],
+  "898": ["n04557648", "water_bottle"],
+  "899": ["n04560804", "water_jug"],
+  "900": ["n04562935", "water_tower"],
+  "901": ["n04579145", "whiskey_jug"],
+  "902": ["n04579432", "whistle"],
+  "903": ["n04584207", "wig"],
+  "904": ["n04589890", "window_screen"],
+  "905": ["n04590129", "window_shade"],
+  "906": ["n04591157", "Windsor_tie"],
+  "907": ["n04591713", "wine_bottle"],
+  "908": ["n04592741", "wing"],
+  "909": ["n04596742", "wok"],
+  "910": ["n04597913", "wooden_spoon"],
+  "911": ["n04599235", "wool"],
+  "912": ["n04604644", "worm_fence"],
+  "913": ["n04606251", "wreck"],
+  "914": ["n04612504", "yawl"],
+  "915": ["n04613696", "yurt"],
+  "916": ["n06359193", "web_site"],
+  "917": ["n06596364", "comic_book"],
+  "918": ["n06785654", "crossword_puzzle"],
+  "919": ["n06794110", "street_sign"],
+  "920": ["n06874185", "traffic_light"],
+  "921": ["n07248320", "book_jacket"],
+  "922": ["n07565083", "menu"],
+  "923": ["n07579787", "plate"],
+  "924": ["n07583066", "guacamole"],
+  "925": ["n07584110", "consomme"],
+  "926": ["n07590611", "hot_pot"],
+  "927": ["n07613480", "trifle"],
+  "928": ["n07614500", "ice_cream"],
+  "929": ["n07615774", "ice_lolly"],
+  "930": ["n07684084", "French_loaf"],
+  "931": ["n07693725", "bagel"],
+  "932": ["n07695742", "pretzel"],
+  "933": ["n07697313", "cheeseburger"],
+  "934": ["n07697537", "hotdog"],
+  "935": ["n07711569", "mashed_potato"],
+  "936": ["n07714571", "head_cabbage"],
+  "937": ["n07714990", "broccoli"],
+  "938": ["n07715103", "cauliflower"],
+  "939": ["n07716358", "zucchini"],
+  "940": ["n07716906", "spaghetti_squash"],
+  "941": ["n07717410", "acorn_squash"],
+  "942": ["n07717556", "butternut_squash"],
+  "943": ["n07718472", "cucumber"],
+  "944": ["n07718747", "artichoke"],
+  "945": ["n07720875", "bell_pepper"],
+  "946": ["n07730033", "cardoon"],
+  "947": ["n07734744", "mushroom"],
+  "948": ["n07742313", "Granny_Smith"],
+  "949": ["n07745940", "strawberry"],
+  "950": ["n07747607", "orange"],
+  "951": ["n07749582", "lemon"],
+  "952": ["n07753113", "fig"],
+  "953": ["n07753275", "pineapple"],
+  "954": ["n07753592", "banana"],
+  "955": ["n07754684", "jackfruit"],
+  "956": ["n07760859", "custard_apple"],
+  "957": ["n07768694", "pomegranate"],
+  "958": ["n07802026", "hay"],
+  "959": ["n07831146", "carbonara"],
+  "960": ["n07836838", "chocolate_sauce"],
+  "961": ["n07860988", "dough"],
+  "962": ["n07871810", "meat_loaf"],
+  "963": ["n07873807", "pizza"],
+  "964": ["n07875152", "potpie"],
+  "965": ["n07880968", "burrito"],
+  "966": ["n07892512", "red_wine"],
+  "967": ["n07920052", "espresso"],
+  "968": ["n07930864", "cup"],
+  "969": ["n07932039", "eggnog"],
+  "970": ["n09193705", "alp"],
+  "971": ["n09229709", "bubble"],
+  "972": ["n09246464", "cliff"],
+  "973": ["n09256479", "coral_reef"],
+  "974": ["n09288635", "geyser"],
+  "975": ["n09332890", "lakeside"],
+  "976": ["n09399592", "promontory"],
+  "977": ["n09421951", "sandbar"],
+  "978": ["n09428293", "seashore"],
+  "979": ["n09468604", "valley"],
+  "980": ["n09472597", "volcano"],
+  "981": ["n09835506", "ballplayer"],
+  "982": ["n10148035", "groom"],
+  "983": ["n10565667", "scuba_diver"],
+  "984": ["n11879895", "rapeseed"],
+  "985": ["n11939491", "daisy"],
+  "986": ["n12057211", "yellow_lady's_slipper"],
+  "987": ["n12144580", "corn"],
+  "988": ["n12267677", "acorn"],
+  "989": ["n12620546", "hip"],
+  "990": ["n12768682", "buckeye"],
+  "991": ["n12985857", "coral_fungus"],
+  "992": ["n12998815", "agaric"],
+  "993": ["n13037406", "gyromitra"],
+  "994": ["n13040303", "stinkhorn"],
+  "995": ["n13044778", "earthstar"],
+  "996": ["n13052670", "hen-of-the-woods"],
+  "997": ["n13054560", "bolete"],
+  "998": ["n13133613", "ear"],
+  "999": ["n15075141", "toilet_tissue"]
+}

+ 51 - 0
machine_learning/app/main.py

@@ -0,0 +1,51 @@
+from typing import Optional
+from pydantic import BaseModel
+import numpy as np
+from fastapi import FastAPI
+import tensorflow as tf
+from tensorflow.keras.applications import InceptionV3
+from tensorflow.keras.applications.inception_v3 import preprocess_input, decode_predictions
+from tensorflow.keras.preprocessing import image
+
+IMG_SIZE = 299
+PREDICTION_MODEL = InceptionV3(weights='imagenet')
+
+
+def warm_up():
+    img_path = f'./app/test.png'
+    img = image.load_img(img_path, target_size=(IMG_SIZE, IMG_SIZE))
+    x = image.img_to_array(img)
+    x = np.expand_dims(x, axis=0)
+    x = preprocess_input(x)
+    PREDICTION_MODEL.predict(x)
+
+
+# Warm up model
+warm_up()
+app = FastAPI()
+
+
+class TagImagePayload(BaseModel):
+    thumbnail_path: str
+
+
+@app.post("/tagImage")
+async def post_root(payload: TagImagePayload):
+    imagePath = payload.thumbnail_path
+
+    if imagePath[0] == '.':
+        imagePath = imagePath[2:]
+
+    img_path = f'./app/{imagePath}'
+    img = image.load_img(img_path, target_size=(IMG_SIZE, IMG_SIZE))
+    x = image.img_to_array(img)
+    x = np.expand_dims(x, axis=0)
+    x = preprocess_input(x)
+
+    preds = PREDICTION_MODEL.predict(x)
+    result = decode_predictions(preds, top=3)[0]
+    payload = []
+    for _, value, _ in result:
+        payload.append(value)
+
+    return payload

+ 0 - 0
design/sc2.PNG → machine_learning/app/test.png


+ 6 - 0
machine_learning/requirements.txt

@@ -0,0 +1,6 @@
+fastapi>=0.68.0,<0.69.0
+pydantic>=1.8.0,<2.0.0
+uvicorn>=0.15.0,<0.16.0
+tensorflow==2.8.0
+numpy==1.22.2
+pillow==9.0.1

+ 95 - 59
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -1,122 +1,158 @@
 {
   "images" : [
     {
-      "size" : "20x20",
+      "filename" : "immich-logo-1024-20@2x.png",
       "idiom" : "iphone",
-      "filename" : "Icon-App-20x20@2x.png",
-      "scale" : "2x"
+      "scale" : "2x",
+      "size" : "20x20"
     },
     {
-      "size" : "20x20",
+      "filename" : "immich-logo-1024-20@3x.png",
       "idiom" : "iphone",
-      "filename" : "Icon-App-20x20@3x.png",
-      "scale" : "3x"
+      "scale" : "3x",
+      "size" : "20x20"
     },
     {
-      "size" : "29x29",
+      "filename" : "immich-logo-1024-29.png",
       "idiom" : "iphone",
-      "filename" : "Icon-App-29x29@1x.png",
-      "scale" : "1x"
+      "scale" : "1x",
+      "size" : "29x29"
     },
     {
-      "size" : "29x29",
+      "filename" : "immich-logo-1024-29@2x.png",
       "idiom" : "iphone",
-      "filename" : "Icon-App-29x29@2x.png",
-      "scale" : "2x"
+      "scale" : "2x",
+      "size" : "29x29"
     },
     {
-      "size" : "29x29",
+      "filename" : "immich-logo-1024-29@3x.png",
       "idiom" : "iphone",
-      "filename" : "Icon-App-29x29@3x.png",
-      "scale" : "3x"
+      "scale" : "3x",
+      "size" : "29x29"
     },
     {
-      "size" : "40x40",
+      "filename" : "immich-logo-1024-40@2x.png",
       "idiom" : "iphone",
-      "filename" : "Icon-App-40x40@2x.png",
-      "scale" : "2x"
+      "scale" : "2x",
+      "size" : "40x40"
     },
     {
-      "size" : "40x40",
+      "filename" : "immich-logo-1024-40@3x.png",
       "idiom" : "iphone",
-      "filename" : "Icon-App-40x40@3x.png",
-      "scale" : "3x"
+      "scale" : "3x",
+      "size" : "40x40"
     },
     {
-      "size" : "60x60",
+      "filename" : "immich-logo-1024-60@2x.png",
       "idiom" : "iphone",
-      "filename" : "Icon-App-60x60@2x.png",
-      "scale" : "2x"
+      "scale" : "2x",
+      "size" : "60x60"
     },
     {
-      "size" : "60x60",
+      "filename" : "immich-logo-1024-60@3x.png",
       "idiom" : "iphone",
-      "filename" : "Icon-App-60x60@3x.png",
-      "scale" : "3x"
+      "scale" : "3x",
+      "size" : "60x60"
     },
     {
-      "size" : "20x20",
-      "idiom" : "ipad",
       "filename" : "Icon-App-20x20@1x.png",
-      "scale" : "1x"
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "20x20"
     },
     {
-      "size" : "20x20",
-      "idiom" : "ipad",
       "filename" : "Icon-App-20x20@2x.png",
-      "scale" : "2x"
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "20x20"
     },
     {
-      "size" : "29x29",
-      "idiom" : "ipad",
       "filename" : "Icon-App-29x29@1x.png",
-      "scale" : "1x"
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "29x29"
     },
     {
-      "size" : "29x29",
-      "idiom" : "ipad",
       "filename" : "Icon-App-29x29@2x.png",
-      "scale" : "2x"
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "29x29"
     },
     {
-      "size" : "40x40",
-      "idiom" : "ipad",
       "filename" : "Icon-App-40x40@1x.png",
-      "scale" : "1x"
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "40x40"
     },
     {
-      "size" : "40x40",
-      "idiom" : "ipad",
       "filename" : "Icon-App-40x40@2x.png",
-      "scale" : "2x"
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "40x40"
     },
     {
-      "size" : "76x76",
-      "idiom" : "ipad",
       "filename" : "Icon-App-76x76@1x.png",
-      "scale" : "1x"
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "76x76"
     },
     {
-      "size" : "76x76",
-      "idiom" : "ipad",
       "filename" : "Icon-App-76x76@2x.png",
-      "scale" : "2x"
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "76x76"
     },
     {
-      "size" : "83.5x83.5",
-      "idiom" : "ipad",
       "filename" : "Icon-App-83.5x83.5@2x.png",
-      "scale" : "2x"
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "83.5x83.5"
     },
     {
-      "size" : "1024x1024",
-      "idiom" : "ios-marketing",
       "filename" : "Icon-App-1024x1024@1x.png",
-      "scale" : "1x"
+      "idiom" : "ios-marketing",
+      "scale" : "1x",
+      "size" : "1024x1024"
+    },
+    {
+      "filename" : "immich-logo-1024-20.png",
+      "idiom" : "universal",
+      "scale" : "1x",
+      "size" : "20x20"
+    },
+    {
+      "filename" : "immich-logo-1024-40.png",
+      "idiom" : "universal",
+      "scale" : "1x",
+      "size" : "40x40"
+    },
+    {
+      "filename" : "immich-logo-1024-76.png",
+      "idiom" : "universal",
+      "scale" : "1x",
+      "size" : "76x76"
+    },
+    {
+      "filename" : "immich-logo-1024-76@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x",
+      "size" : "76x76"
+    },
+    {
+      "filename" : "immich-logo-1024-83.5@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x",
+      "size" : "83.5x83.5"
+    },
+    {
+      "filename" : "immich-logo-1024-1024.png",
+      "idiom" : "universal",
+      "scale" : "1x",
+      "size" : "1024x1024"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   }
 }

BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-1024.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-20.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-20@2x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-20@3x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-29.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-29@2x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-29@3x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-40.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-40@2x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-40@3x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-60@2x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-60@3x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-76.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-76@2x.png


BIN
mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/immich-logo-1024-83.5@2x.png


+ 2 - 0
server/entrypoint.sh

@@ -0,0 +1,2 @@
+# npm run typeorm migration:run
+npm run start:dev

Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 10530
server/package-lock.json


+ 5 - 1
server/package.json

@@ -18,7 +18,8 @@
     "test:watch": "jest --watch",
     "test:cov": "jest --coverage",
     "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
-    "test:e2e": "jest --config ./test/jest-e2e.json"
+    "test:e2e": "jest --config ./test/jest-e2e.json",
+    "typeorm": "node --require ts-node/register ./node_modules/typeorm/cli.js"
   },
   "dependencies": {
     "@nestjs/bull": "^0.4.2",
@@ -34,6 +35,7 @@
     "@nestjs/typeorm": "^8.0.3",
     "@nestjs/websockets": "^8.2.6",
     "@socket.io/redis-adapter": "^7.1.0",
+    "axios": "^0.26.0",
     "bcrypt": "^5.0.1",
     "bull": "^4.4.0",
     "class-transformer": "^0.5.1",
@@ -46,10 +48,12 @@
     "passport": "^0.5.2",
     "passport-jwt": "^4.0.0",
     "pg": "^8.7.1",
+    "redis": "^3.1.2",
     "reflect-metadata": "^0.1.13",
     "rimraf": "^3.0.2",
     "rxjs": "^7.2.0",
     "sharp": "0.28",
+    "socket.io-redis": "^6.1.1",
     "systeminformation": "^5.11.0",
     "typeorm": "^0.2.41"
   },

+ 4 - 0
server/src/api-v1/asset/entities/asset.entity.ts

@@ -1,5 +1,6 @@
 import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn, PrimaryGeneratedColumn, Unique } from 'typeorm';
 import { ExifEntity } from './exif.entity';
+import { SmartInfoEntity } from './smart-info.entity';
 
 @Entity('assets')
 @Unique(['deviceAssetId', 'userId', 'deviceId'])
@@ -42,6 +43,9 @@ export class AssetEntity {
 
   @OneToOne(() => ExifEntity, (exifEntity) => exifEntity.asset)
   exifInfo: ExifEntity;
+
+  @OneToOne(() => SmartInfoEntity, (smartInfoEntity) => smartInfoEntity.asset)
+  smartInfo: SmartInfoEntity;
 }
 
 export enum AssetType {

+ 19 - 0
server/src/api-v1/asset/entities/smart-info.entity.ts

@@ -0,0 +1,19 @@
+import { Column, Entity, Index, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
+import { AssetEntity } from './asset.entity';
+
+@Entity('smart_info')
+export class SmartInfoEntity {
+  @PrimaryGeneratedColumn()
+  id: string;
+
+  @Index({ unique: true })
+  @Column({ type: 'uuid' })
+  assetId: string;
+
+  @Column({ type: 'text', array: true, nullable: true })
+  tags: string[];
+
+  @OneToOne(() => AssetEntity, { onDelete: 'CASCADE', nullable: true })
+  @JoinColumn({ name: 'assetId', referencedColumnName: 'id' })
+  asset: SmartInfoEntity;
+}

+ 11 - 15
server/src/config/database.config.ts

@@ -1,12 +1,11 @@
 import { TypeOrmModuleOptions } from '@nestjs/typeorm';
-import dotenv from 'dotenv';
+// import dotenv from 'dotenv';
 
-const result = dotenv.config();
-
-if (result.error) {
-  console.log(result.error);
-}
+// const result = dotenv.config();
 
+// if (result.error) {
+//   console.log(result.error);
+// }
 export const databaseConfig: TypeOrmModuleOptions = {
   type: 'postgres',
   host: 'immich_postgres',
@@ -15,13 +14,10 @@ export const databaseConfig: TypeOrmModuleOptions = {
   password: process.env.DB_PASSWORD,
   database: process.env.DB_DATABASE_NAME,
   entities: [__dirname + '/../**/*.entity.{js,ts}'],
-  synchronize: true,
-  // logging: true,
-  // logger: 'advanced-console',
-  // ssl: process.env.NODE_ENV == 'production',
-  // extra: {
-  //   ssl: {
-  //     rejectUnauthorized: false,
-  //   },
-  // },
+  synchronize: false,
+  migrations: [__dirname + '/../migration/*.js'],
+  cli: {
+    migrationsDir: __dirname + '/../migration',
+  },
+  migrationsRun: true,
 };

+ 12 - 4
server/src/middlewares/redis-io.adapter.middleware.ts

@@ -1,15 +1,23 @@
 import { IoAdapter } from '@nestjs/platform-socket.io';
-import { RedisClient, createClient } from 'redis';
+import { RedisClient } from 'redis';
 import { ServerOptions } from 'socket.io';
-import { createAdapter } from '@socket.io/redis-adapter';
+import { createAdapter } from 'socket.io-redis';
+
+// const pubClient = createClient({ url: 'redis://immich_redis:6379' });
+// const subClient = pubClient.duplicate();
+
+const pubClient = new RedisClient({
+  port: 6379,
+  host: 'immich_redis',
+});
 
-const pubClient = createClient({ url: 'redis://immich_redis:6379' });
 const subClient = pubClient.duplicate();
+const redisAdapter = createAdapter({ pubClient, subClient });
 
 export class RedisIoAdapter extends IoAdapter {
   createIOServer(port: number, options?: ServerOptions): any {
     const server = super.createIOServer(port, options);
-    server.adapter(createAdapter(pubClient, subClient));
+    server.adapter(redisAdapter);
     return server;
   }
 }

+ 22 - 0
server/src/migration/1645130759468-CreateUserTable.ts

@@ -0,0 +1,22 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class CreateUserTable1645130759468 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`
+        create table if not exists users
+        (
+            id          uuid      default uuid_generate_v4() not null
+                constraint "PK_a3ffb1c0c8416b9fc6f907b7433"
+                    primary key,
+            email       varchar                              not null,
+            password    varchar                              not null,
+            salt        varchar                              not null,
+            "createdAt" timestamp default now()              not null
+        );
+      `);
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`drop table users`);
+  }
+}

+ 26 - 0
server/src/migration/1645130777674-CreateDeviceInfoTable.ts

@@ -0,0 +1,26 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class CreateDeviceInfoTable1645130777674 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`
+        create table if not exists device_info
+        (
+            id                  serial
+                constraint "PK_b1c15a80b0a4e5f4eebadbdd92c"
+                    primary key,
+            "userId"            varchar                 not null,
+            "deviceId"          varchar                 not null,
+            "deviceType"        varchar                 not null,
+            "notificationToken" varchar,
+            "createdAt"         timestamp default now() not null,
+            "isAutoBackup"      boolean   default false not null,
+            constraint "UQ_ebad78f36b10d15fbea8560e107"
+                unique ("userId", "deviceId")
+        );
+      `);
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`drop table device_info`);
+  }
+}

+ 31 - 0
server/src/migration/1645130805273-CreateAssetsTable.ts

@@ -0,0 +1,31 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class CreateAssetsTable1645130805273 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`
+        create table if not exists assets
+        (
+            id              uuid    default uuid_generate_v4() not null
+                constraint "PK_da96729a8b113377cfb6a62439c"
+                    primary key,
+            "deviceAssetId" varchar                            not null,
+            "userId"        varchar                            not null,
+            "deviceId"      varchar                            not null,
+            type            varchar                            not null,
+            "originalPath"  varchar                            not null,
+            "resizePath"    varchar,
+            "createdAt"     varchar                            not null,
+            "modifiedAt"    varchar                            not null,
+            "isFavorite"    boolean default false              not null,
+            "mimeType"      varchar,
+            duration        varchar,
+            constraint "UQ_b599ab0bd9574958acb0b30a90e"
+                unique ("deviceAssetId", "userId", "deviceId")
+        );
+      `);
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`drop table assets`);
+  }
+}

+ 42 - 0
server/src/migration/1645130817965-CreateExifTable.ts

@@ -0,0 +1,42 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class CreateExifTable1645130817965 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`
+        create table if not exists exif
+        (
+            id                 serial
+                constraint "PK_28663352d85078ad0046dafafaa"
+                    primary key,
+            "assetId"          uuid not null
+                constraint "REL_c0117fdbc50b917ef9067740c4"
+                    unique
+                constraint "FK_c0117fdbc50b917ef9067740c44"
+                    references assets
+                    on delete cascade,
+            make               varchar,
+            model              varchar,
+            "imageName"        varchar,
+            "exifImageWidth"   integer,
+            "exifImageHeight"  integer,
+            "fileSizeInByte"   integer,
+            orientation        varchar,
+            "dateTimeOriginal" timestamp with time zone,
+            "modifyDate"       timestamp with time zone,
+            "lensModel"        varchar,
+            "fNumber"          double precision,
+            "focalLength"      double precision,
+            iso                integer,
+            "exposureTime"     double precision,
+            latitude           double precision,
+            longitude          double precision
+        );
+
+        create unique index if not exists "IDX_c0117fdbc50b917ef9067740c4" on exif ("assetId");
+      `);
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`drop table exif`);
+  }
+}

+ 30 - 0
server/src/migration/1645130870184-CreateSmartInfoTable.ts

@@ -0,0 +1,30 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class CreateSmartInfoTable1645130870184 implements MigrationInterface {
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`
+        create table if not exists smart_info
+        (
+            id        serial
+                constraint "PK_0beace66440e9713f5c40470e46"
+                    primary key,
+            "assetId" uuid not null
+                constraint "UQ_5e3753aadd956110bf3ec0244ac"
+                    unique
+                constraint "FK_5e3753aadd956110bf3ec0244ac"
+                    references assets
+                    on delete cascade,
+            tags      text[]
+        );
+
+        create unique index if not exists "IDX_5e3753aadd956110bf3ec0244a"
+          on smart_info ("assetId");
+      `);
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query(`
+        drop table smart_info;
+      `);
+  }
+}

+ 2 - 1
server/src/modules/background-task/background-task.module.ts

@@ -3,6 +3,7 @@ import { Module } from '@nestjs/common';
 import { TypeOrmModule } from '@nestjs/typeorm';
 import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
 import { ExifEntity } from '../../api-v1/asset/entities/exif.entity';
+import { SmartInfoEntity } from '../../api-v1/asset/entities/smart-info.entity';
 import { BackgroundTaskProcessor } from './background-task.processor';
 import { BackgroundTaskService } from './background-task.service';
 
@@ -16,7 +17,7 @@ import { BackgroundTaskService } from './background-task.service';
         removeOnFail: false,
       },
     }),
-    TypeOrmModule.forFeature([AssetEntity, ExifEntity]),
+    TypeOrmModule.forFeature([AssetEntity, ExifEntity, SmartInfoEntity]),
   ],
   providers: [BackgroundTaskService, BackgroundTaskProcessor],
   exports: [BackgroundTaskService],

+ 19 - 0
server/src/modules/background-task/background-task.processor.ts

@@ -9,6 +9,8 @@ import { readFile } from 'fs/promises';
 import fs from 'fs';
 import { Logger } from '@nestjs/common';
 import { ExifEntity } from '../../api-v1/asset/entities/exif.entity';
+import axios from 'axios';
+import { SmartInfoEntity } from '../../api-v1/asset/entities/smart-info.entity';
 
 @Processor('background-task')
 export class BackgroundTaskProcessor {
@@ -16,6 +18,9 @@ export class BackgroundTaskProcessor {
     @InjectRepository(AssetEntity)
     private assetRepository: Repository<AssetEntity>,
 
+    @InjectRepository(SmartInfoEntity)
+    private smartInfoRepository: Repository<SmartInfoEntity>,
+
     @InjectRepository(ExifEntity)
     private exifRepository: Repository<ExifEntity>,
 
@@ -76,4 +81,18 @@ export class BackgroundTaskProcessor {
       });
     });
   }
+
+  @Process('tag-image')
+  async tagImage(job) {
+    const { thumbnailPath, asset }: { thumbnailPath: string; asset: AssetEntity } = job.data;
+    const res = await axios.post('http://immich_tf_fastapi:8000/tagImage', { thumbnail_path: thumbnailPath });
+
+    if (res.status == 200) {
+      const smartInfo = new SmartInfoEntity();
+      smartInfo.assetId = asset.id;
+      smartInfo.tags = [...res.data];
+
+      this.smartInfoRepository.save(smartInfo);
+    }
+  }
 }

+ 11 - 0
server/src/modules/background-task/background-task.service.ts

@@ -32,4 +32,15 @@ export class BackgroundTaskService {
       { jobId: randomUUID() },
     );
   }
+
+  async tagImage(thumbnailPath: string, asset: AssetEntity) {
+    await this.backgroundTaskQueue.add(
+      'tag-image',
+      {
+        thumbnailPath,
+        asset,
+      },
+      { jobId: randomUUID() },
+    );
+  }
 }

+ 12 - 8
server/src/modules/image-optimize/image-optimize.module.ts

@@ -1,20 +1,17 @@
 import { BullModule } from '@nestjs/bull';
 import { Module } from '@nestjs/common';
 import { TypeOrmModule } from '@nestjs/typeorm';
-import { join } from 'path';
-import { AssetModule } from '../../api-v1/asset/asset.module';
-import { AssetService } from '../../api-v1/asset/asset.service';
 import { AssetEntity } from '../../api-v1/asset/entities/asset.entity';
-import { CommunicationGateway } from '../../api-v1/communication/communication.gateway';
 import { CommunicationModule } from '../../api-v1/communication/communication.module';
-import { UserEntity } from '../../api-v1/user/entities/user.entity';
-import { ImmichJwtModule } from '../immich-jwt/immich-jwt.module';
+import { BackgroundTaskModule } from '../background-task/background-task.module';
+import { BackgroundTaskService } from '../background-task/background-task.service';
 import { ImageOptimizeProcessor } from './image-optimize.processor';
 import { AssetOptimizeService } from './image-optimize.service';
 
 @Module({
   imports: [
     CommunicationModule,
+    BackgroundTaskModule,
     BullModule.registerQueue({
       name: 'optimize',
       defaultJobOptions: {
@@ -23,10 +20,17 @@ import { AssetOptimizeService } from './image-optimize.service';
         removeOnFail: false,
       },
     }),
-
+    BullModule.registerQueue({
+      name: 'background-task',
+      defaultJobOptions: {
+        attempts: 3,
+        removeOnComplete: true,
+        removeOnFail: false,
+      },
+    }),
     TypeOrmModule.forFeature([AssetEntity]),
   ],
-  providers: [AssetOptimizeService, ImageOptimizeProcessor],
+  providers: [AssetOptimizeService, ImageOptimizeProcessor, BackgroundTaskService],
   exports: [AssetOptimizeService],
 })
 export class ImageOptimizeModule {}

+ 16 - 0
server/src/modules/image-optimize/image-optimize.processor.ts

@@ -11,6 +11,7 @@ import { APP_UPLOAD_LOCATION } from '../../constants/upload_location.constant';
 import { WebSocketServer } from '@nestjs/websockets';
 import { Socket, Server as SocketIoServer } from 'socket.io';
 import { CommunicationGateway } from '../../api-v1/communication/communication.gateway';
+import { BackgroundTaskService } from '../background-task/background-task.service';
 
 @Processor('optimize')
 export class ImageOptimizeProcessor {
@@ -18,6 +19,8 @@ export class ImageOptimizeProcessor {
     private wsCommunicateionGateway: CommunicationGateway,
     @InjectRepository(AssetEntity)
     private assetRepository: Repository<AssetEntity>,
+
+    private backgroundTaskService: BackgroundTaskService,
   ) {}
 
   @Process('resize-image')
@@ -58,11 +61,15 @@ export class ImageOptimizeProcessor {
             }
 
             const res = await this.assetRepository.update(savedAsset, { resizePath: desitnation });
+
             if (res.affected) {
               this.wsCommunicateionGateway.server
                 .to(savedAsset.userId)
                 .emit('on_upload_success', JSON.stringify(savedAsset));
             }
+
+            // Tag Image
+            this.backgroundTaskService.tagImage(desitnation, savedAsset);
           });
       } else {
         sharp(data)
@@ -79,6 +86,9 @@ export class ImageOptimizeProcessor {
                 .to(savedAsset.userId)
                 .emit('on_upload_success', JSON.stringify(savedAsset));
             }
+
+            // Tag Image
+            this.backgroundTaskService.tagImage(resizePath, savedAsset);
           });
       }
     });
@@ -107,12 +117,18 @@ export class ImageOptimizeProcessor {
         filename: `${filename}.png`,
       })
       .on('end', async (a) => {
+        const thumbnailPath = `${resizeDir}/${filename}.png`;
+
         const res = await this.assetRepository.update(savedAsset, { resizePath: `${resizeDir}/${filename}.png` });
+
         if (res.affected) {
           this.wsCommunicateionGateway.server
             .to(savedAsset.userId)
             .emit('on_upload_success', JSON.stringify(savedAsset));
         }
+
+        // Tag Image
+        this.backgroundTaskService.tagImage(thumbnailPath, savedAsset);
       });
 
     return 'ok';

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.