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
6
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
|
||||
|
12
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`
|
||||
|
1
docker/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.env
|
95
docker/docker-compose.gpu.yml
Normal file
@ -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:
|
@ -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
docker/settings/nginx-conf/nginx.conf
Normal file
@ -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
machine_learning/.dockerignore
Normal file
@ -0,0 +1 @@
|
||||
devenv/
|
2
machine_learning/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
app/__pycache__
|
||||
/devenv
|
22
machine_learning/Dockerfile
Normal file
@ -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
machine_learning/Pipfile
Normal file
@ -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
machine_learning/Pipfile.lock
generated
Normal file
@ -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
machine_learning/app/imagenet_class_index.json
Normal file
51
machine_learning/app/main.py
Normal file
@ -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
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 345 KiB |
6
machine_learning/requirements.txt
Normal file
@ -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
|
@ -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"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 118 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 19 KiB |
2
server/entrypoint.sh
Normal file
@ -0,0 +1,2 @@
|
||||
# npm run typeorm migration:run
|
||||
npm run start:dev
|
10598
server/package-lock.json
generated
@ -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"
|
||||
},
|
||||
|
@ -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
server/src/api-v1/asset/entities/smart-info.entity.ts
Normal file
@ -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;
|
||||
}
|
@ -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,
|
||||
};
|
||||
|
@ -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
server/src/migration/1645130759468-CreateUserTable.ts
Normal file
@ -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
server/src/migration/1645130777674-CreateDeviceInfoTable.ts
Normal file
@ -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
server/src/migration/1645130805273-CreateAssetsTable.ts
Normal file
@ -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
server/src/migration/1645130817965-CreateExifTable.ts
Normal file
@ -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
server/src/migration/1645130870184-CreateSmartInfoTable.ts
Normal file
@ -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;
|
||||
`);
|
||||
}
|
||||
}
|
@ -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],
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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';
|
||||
|