Initial commit: added frontend and backend
@@ -0,0 +1,13 @@
|
||||
# ==========================================
|
||||
# Frontend Environment Configuration
|
||||
# ==========================================
|
||||
|
||||
# --- Local Development Options ---
|
||||
PORT=8088
|
||||
REACT_APP_API_URL=http://localhost:8089
|
||||
DANGEROUSLY_DISABLE_HOST_CHECK=true
|
||||
|
||||
# --- Production Options ---
|
||||
# By default, config.js falls back to https://digifox.studio
|
||||
# Uncomment the line below when you run `npm run build` for production:
|
||||
# REACT_APP_API_URL=https://digifox.studio
|
||||
@@ -0,0 +1,26 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
backend/
|
||||
|
||||
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
# /build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -0,0 +1,70 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/skinbook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open the app in your browser to view it.
|
||||
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://skinbook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://skinbook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||
|
||||
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||
|
||||
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://skinbook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
### Code Splitting
|
||||
|
||||
This section has moved here: [https://skinbook.github.io/create-react-app/docs/code-splitting](https://skinbook.github.io/create-react-app/docs/code-splitting)
|
||||
|
||||
### Analyzing the Bundle Size
|
||||
|
||||
This section has moved here: [https://skinbook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://skinbook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||
|
||||
### Making a Progressive Web App
|
||||
|
||||
This section has moved here: [https://skinbook.github.io/create-react-app/docs/making-a-progressive-web-app](https://skinbook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This section has moved here: [https://skinbook.github.io/create-react-app/docs/advanced-configuration](https://skinbook.github.io/create-react-app/docs/advanced-configuration)
|
||||
|
||||
### Deployment
|
||||
|
||||
This section has moved here: [https://skinbook.github.io/create-react-app/docs/deployment](https://skinbook.github.io/create-react-app/docs/deployment)
|
||||
|
||||
### `npm run build` fails to minify
|
||||
|
||||
This section has moved here: [https://skinbook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://skinbook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/elixifyskinanalyser/static/css/main.e120d544.css",
|
||||
"main.js": "/elixifyskinanalyser/static/js/main.c8d4b79e.js",
|
||||
"static/js/977.2e3d5464.chunk.js": "/elixifyskinanalyser/static/js/977.2e3d5464.chunk.js",
|
||||
"static/js/455.8da17214.chunk.js": "/elixifyskinanalyser/static/js/455.8da17214.chunk.js",
|
||||
"static/media/albert-sans-latin-400-normal.woff": "/elixifyskinanalyser/static/media/albert-sans-latin-400-normal.888d12cf35571cc0b5b9.woff",
|
||||
"static/media/albert-sans-latin-ext-400-normal.woff": "/elixifyskinanalyser/static/media/albert-sans-latin-ext-400-normal.be313b5a23da125a5d96.woff",
|
||||
"static/media/albert-sans-latin-400-normal.woff2": "/elixifyskinanalyser/static/media/albert-sans-latin-400-normal.90c74757f3a31237a41d.woff2",
|
||||
"static/media/albert-sans-latin-ext-400-normal.woff2": "/elixifyskinanalyser/static/media/albert-sans-latin-ext-400-normal.87d26317c9e3d5606fa8.woff2",
|
||||
"index.html": "/elixifyskinanalyser/index.html",
|
||||
"main.e120d544.css.map": "/elixifyskinanalyser/static/css/main.e120d544.css.map",
|
||||
"main.c8d4b79e.js.map": "/elixifyskinanalyser/static/js/main.c8d4b79e.js.map",
|
||||
"977.2e3d5464.chunk.js.map": "/elixifyskinanalyser/static/js/977.2e3d5464.chunk.js.map",
|
||||
"455.8da17214.chunk.js.map": "/elixifyskinanalyser/static/js/455.8da17214.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.e120d544.css",
|
||||
"static/js/main.c8d4b79e.js"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"age_spot": {
|
||||
"raw_score": 98.26628875732422,
|
||||
"ui_score": 96,
|
||||
"output_mask_name": "age_spot_output.png"
|
||||
},
|
||||
"radiance": {
|
||||
"raw_score": 97.64312505722046,
|
||||
"ui_score": 96,
|
||||
"output_mask_name": "radiance_output.png"
|
||||
},
|
||||
"oiliness": {
|
||||
"raw_score": 83.94287872314453,
|
||||
"ui_score": 82,
|
||||
"output_mask_name": "oiliness_output.png"
|
||||
},
|
||||
"firmness": {
|
||||
"raw_score": 89.18848633766174,
|
||||
"ui_score": 85,
|
||||
"output_mask_name": "firmness_output.png"
|
||||
},
|
||||
"dark_circle_v2": {
|
||||
"raw_score": 55.23899793624878,
|
||||
"ui_score": 67,
|
||||
"output_mask_name": "dark_circle_v2_output.png"
|
||||
},
|
||||
"droopy_lower_eyelid": {
|
||||
"raw_score": 80.4837703704834,
|
||||
"ui_score": 81,
|
||||
"output_mask_name": "droopy_lower_eyelid_output.png"
|
||||
},
|
||||
"droopy_upper_eyelid": {
|
||||
"raw_score": 82.7034592628479,
|
||||
"ui_score": 82,
|
||||
"output_mask_name": "droopy_upper_eyelid_output.png"
|
||||
},
|
||||
"wrinkle": {
|
||||
"raw_score": 95.83742618560791,
|
||||
"ui_score": 91,
|
||||
"output_mask_name": "wrinkle_output.png"
|
||||
},
|
||||
"redness": {
|
||||
"raw_score": 100.0,
|
||||
"ui_score": 99,
|
||||
"output_mask_name": "redness_output.png"
|
||||
},
|
||||
"pore": {
|
||||
"raw_score": 99.96079206466675,
|
||||
"ui_score": 99,
|
||||
"output_mask_name": "pore_output.png"
|
||||
},
|
||||
"eye_bag": {
|
||||
"raw_score": 88.28426599502563,
|
||||
"ui_score": 84,
|
||||
"output_mask_name": "eye_bag_output.png"
|
||||
},
|
||||
"texture": {
|
||||
"raw_score": 97.28496074676514,
|
||||
"ui_score": 94,
|
||||
"output_mask_name": "texture_output.png"
|
||||
},
|
||||
"moisture": {
|
||||
"raw_score": 71.30312919616699,
|
||||
"ui_score": 78,
|
||||
"output_mask_name": "moisture_output.png"
|
||||
},
|
||||
"acne": {
|
||||
"raw_score": 100.0,
|
||||
"ui_score": 99,
|
||||
"output_mask_name": "acne_output.png"
|
||||
},
|
||||
"all": {
|
||||
"score": 88.07142857142857
|
||||
},
|
||||
"skin_age": 26
|
||||
}
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 294 KiB |
|
After Width: | Height: | Size: 398 KiB |
|
After Width: | Height: | Size: 408 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 765 B |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 306 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Skin Analysis</title><script src="https://cdn.tailwindcss.com"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/3.3.2/jsencrypt.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"><style>@keyframes fadeIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.fade-in{animation:fadeIn .6s ease-out forwards}.pulse{animation:pulse 2s infinite}@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(147,197,253,.7)}70%{box-shadow:0 0 0 10px rgba(147,197,253,0)}100%{box-shadow:0 0 0 0 rgba(147,197,253,0)}}.image-preview{transition:all .3s ease}.image-preview:hover{transform:scale(1.02)}.btn-hover{transition:all .3s ease}.btn-hover:hover{transform:translateY(-2px)}</style><script defer="defer" src="/elixifyskinanalyser/static/js/main.c8d4b79e.js"></script><link href="/elixifyskinanalyser/static/css/main.e120d544.css" rel="stylesheet"></head><body class="bg-white font-sans"><div id="root"></div></body></html>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
[
|
||||
{
|
||||
"weights": [
|
||||
{
|
||||
"name": "dense0/conv0/filters",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
32
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.008194216092427571,
|
||||
"min": -0.9423348506291708
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense0/conv0/bias",
|
||||
"shape": [
|
||||
32
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.006839508168837603,
|
||||
"min": -0.8412595047670252
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense0/conv1/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
32,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.009194007106855804,
|
||||
"min": -1.2779669878529567
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense0/conv1/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
32,
|
||||
32
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0036026100317637128,
|
||||
"min": -0.3170296827952067
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense0/conv1/bias",
|
||||
"shape": [
|
||||
32
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.000740380117706224,
|
||||
"min": -0.06367269012273527
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense0/conv2/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
32,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 1,
|
||||
"min": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense0/conv2/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
32,
|
||||
32
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 1,
|
||||
"min": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense0/conv2/bias",
|
||||
"shape": [
|
||||
32
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0037702228508743585,
|
||||
"min": -0.6220867703942692
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense1/conv0/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
32,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0033707996209462483,
|
||||
"min": -0.421349952618281
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense1/conv0/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
32,
|
||||
64
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.014611541991140328,
|
||||
"min": -1.8556658328748217
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense1/conv0/bias",
|
||||
"shape": [
|
||||
64
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.002832523046755323,
|
||||
"min": -0.30307996600281956
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense1/conv1/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
64,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.006593170586754294,
|
||||
"min": -0.6329443763284123
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense1/conv1/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
64,
|
||||
64
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.012215249211180444,
|
||||
"min": -1.6001976466646382
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense1/conv1/bias",
|
||||
"shape": [
|
||||
64
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.002384825547536214,
|
||||
"min": -0.3028728445370992
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense1/conv2/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
64,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.005859645441466687,
|
||||
"min": -0.7617539073906693
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense1/conv2/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
64,
|
||||
64
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.013121426806730382,
|
||||
"min": -1.7845140457153321
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense1/conv2/bias",
|
||||
"shape": [
|
||||
64
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0032247188044529336,
|
||||
"min": -0.46435950784122243
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense2/conv0/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
64,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.002659512618008782,
|
||||
"min": -0.32977956463308894
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense2/conv0/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
64,
|
||||
128
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.015499923743453681,
|
||||
"min": -1.9839902391620712
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense2/conv0/bias",
|
||||
"shape": [
|
||||
128
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0032450980999890497,
|
||||
"min": -0.522460794098237
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense2/conv1/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
128,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.005911862382701799,
|
||||
"min": -0.792189559282041
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense2/conv1/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
128,
|
||||
128
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.021025861478319356,
|
||||
"min": -2.2077154552235325
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense2/conv1/bias",
|
||||
"shape": [
|
||||
128
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.00349616945958605,
|
||||
"min": -0.46149436866535865
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense2/conv2/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
128,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.008104994250278847,
|
||||
"min": -1.013124281284856
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense2/conv2/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
128,
|
||||
128
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.029337059282789044,
|
||||
"min": -3.5791212325002633
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dense2/conv2/bias",
|
||||
"shape": [
|
||||
128
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0038808938334969913,
|
||||
"min": -0.4230174278511721
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fc/weights",
|
||||
"shape": [
|
||||
128,
|
||||
136
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.014016061670639936,
|
||||
"min": -1.8921683255363912
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fc/bias",
|
||||
"shape": [
|
||||
136
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0029505149698724935,
|
||||
"min": 0.088760145008564
|
||||
}
|
||||
}
|
||||
],
|
||||
"paths": [
|
||||
"face_landmark_68_tiny_model-shard1"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,273 @@
|
||||
[
|
||||
{
|
||||
"weights": [
|
||||
{
|
||||
"name": "conv0/filters",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
16
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.009007044399485869,
|
||||
"min": -1.2069439495311063
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv0/bias",
|
||||
"shape": [
|
||||
16
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.005263455241334205,
|
||||
"min": -0.9211046672334858
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv1/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
16,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.004001977630690033,
|
||||
"min": -0.5042491814669441
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv1/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
16,
|
||||
32
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.013836609615999109,
|
||||
"min": -1.411334180831909
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv1/bias",
|
||||
"shape": [
|
||||
32
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0015159862590771096,
|
||||
"min": -0.30926119685173037
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv2/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
32,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.002666276225856706,
|
||||
"min": -0.317286870876948
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv2/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
32,
|
||||
64
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.015265831292844286,
|
||||
"min": -1.6792414422128714
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv2/bias",
|
||||
"shape": [
|
||||
64
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0020280554598453,
|
||||
"min": -0.37113414915168985
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv3/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
64,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.006100742489683862,
|
||||
"min": -0.8907084034938438
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv3/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
64,
|
||||
128
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.016276211832083907,
|
||||
"min": -2.0508026908425725
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv3/bias",
|
||||
"shape": [
|
||||
128
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.003394414279975143,
|
||||
"min": -0.7637432129944072
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv4/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
128,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.006716050119961009,
|
||||
"min": -0.8059260143953211
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv4/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
128,
|
||||
256
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.021875603993733724,
|
||||
"min": -2.8875797271728514
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv4/bias",
|
||||
"shape": [
|
||||
256
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.0041141652009066415,
|
||||
"min": -0.8187188749804216
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv5/depthwise_filter",
|
||||
"shape": [
|
||||
3,
|
||||
3,
|
||||
256,
|
||||
1
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.008423839597141042,
|
||||
"min": -0.9013508368940915
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv5/pointwise_filter",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
256,
|
||||
512
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.030007277283014035,
|
||||
"min": -3.8709387695088107
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv5/bias",
|
||||
"shape": [
|
||||
512
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.008402082966823203,
|
||||
"min": -1.4871686851277068
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv8/filters",
|
||||
"shape": [
|
||||
1,
|
||||
1,
|
||||
512,
|
||||
25
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.028336129469030042,
|
||||
"min": -4.675461362389957
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "conv8/bias",
|
||||
"shape": [
|
||||
25
|
||||
],
|
||||
"dtype": "float32",
|
||||
"quantization": {
|
||||
"dtype": "uint8",
|
||||
"scale": 0.002268134028303857,
|
||||
"min": -0.41053225912299807
|
||||
}
|
||||
}
|
||||
],
|
||||
"paths": [
|
||||
"tiny_face_detector_model-shard1"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,493 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>skin Analysis</title>
|
||||
<!-- Tailwind CSS for styling -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- Font Awesome for icons -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- JSZip for handling the mask zip file -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
||||
<!-- JSEncrypt for API authentication -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/3.3.2/jsencrypt.min.js"></script>
|
||||
|
||||
<style>
|
||||
/* Custom animations for a smooth user experience */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.fade-in {
|
||||
animation: fadeIn 0.6s ease-out forwards;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% { box-shadow: 0 0 0 0 rgba(147, 197, 253, 0.7); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(147, 197, 253, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(147, 197, 253, 0); }
|
||||
}
|
||||
.pulse {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gradient-to-br from-blue-50 to-green-50 min-h-screen font-sans text-gray-800">
|
||||
|
||||
<div class="container mx-auto px-4 py-12">
|
||||
<!-- Header Section -->
|
||||
<header class="text-center mb-12 fade-in">
|
||||
<h1 class="text-4xl md:text-5xl font-bold text-blue-800 mb-4">AI-Powered Skin Analysis</h1>
|
||||
<p class="text-lg text-gray-600 max-w-2xl mx-auto">
|
||||
Upload a photo or take one instantly to get your skin analysis powered by AI.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main class="max-w-4xl mx-auto">
|
||||
<div id="appContainer" class="bg-white rounded-2xl shadow-xl overflow-hidden fade-in">
|
||||
<div class="p-8 md:p-12">
|
||||
|
||||
<!-- Initial View: Upload/Camera Buttons -->
|
||||
<div id="initialView" class="flex flex-col sm:flex-row gap-4 mb-8 justify-center">
|
||||
<button id="uploadBtn" class="btn-hover flex-1 bg-blue-100 hover:bg-blue-200 text-blue-800 font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center gap-2">
|
||||
<i class="fas fa-upload"></i> Upload Photo
|
||||
</button>
|
||||
<button id="cameraBtn" class="btn-hover flex-1 bg-green-100 hover:bg-green-200 text-green-800 font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center gap-2">
|
||||
<i class="fas fa-camera"></i> Take Photo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- File Input (Hidden) -->
|
||||
<input type="file" id="fileInput" accept="image/*" class="hidden">
|
||||
|
||||
<!-- Camera Preview View -->
|
||||
<div id="cameraView" class="hidden mb-8">
|
||||
<div class="relative bg-gray-100 rounded-lg overflow-hidden h-64 md:h-96">
|
||||
<video id="cameraPreview" class="w-full h-full object-cover"></video>
|
||||
<button id="captureBtn" class="absolute bottom-4 left-1/2 transform -translate-x-1/2 bg-white rounded-full p-3 shadow-lg pulse">
|
||||
<i class="fas fa-circle text-red-500 text-2xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image Preview View -->
|
||||
<div id="previewView" class="hidden mb-8">
|
||||
<h3 class="text-xl font-semibold text-gray-700 mb-4">Your Photo</h3>
|
||||
<div class="image-preview bg-gray-100 rounded-lg overflow-hidden flex justify-center items-center h-64 md:h-96">
|
||||
<img id="previewImage" class="w-full h-full object-contain" alt="Your skin photo" />
|
||||
<canvas id="hiddenCanvas" class="hidden"></canvas>
|
||||
</div>
|
||||
<button id="retakeBtn" class="mt-4 text-blue-600 hover:text-blue-800 flex items-center gap-2">
|
||||
<i class="fas fa-redo"></i> Retake Photo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button & Loading Indicator -->
|
||||
<div id="submitContainer" class="hidden text-center mt-8">
|
||||
<button id="submitBtn" class="btn-hover bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-8 rounded-lg transition-all transform hover:scale-105">
|
||||
Analyze My skin <i class="fas fa-arrow-right ml-2"></i>
|
||||
</button>
|
||||
<p id="loadingIndicator" class="hidden mt-4 text-gray-500">Processing...</p>
|
||||
<p id="errorMsg" class="hidden mt-4 text-red-600"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Analysis Results Section (Initially Hidden) -->
|
||||
<section id="resultsSection" class="hidden mt-16 max-w-5xl mx-auto fade-in">
|
||||
<h2 class="text-2xl font-bold text-blue-800 mb-6">Detailed skin Scores</h2>
|
||||
<div class="flex flex-col md:flex-row gap-6">
|
||||
<!-- Image with Overlay -->
|
||||
<div class="relative w-full md:w-1/2">
|
||||
<img id="resultImage" src="#" alt="Uploaded skin" class="w-full rounded-lg border shadow"/>
|
||||
<img id="maskImage" src="#" alt="Analysis mask" class="absolute top-0 left-0 w-full h-full object-contain opacity-70 hidden"/>
|
||||
</div>
|
||||
<!-- Score List -->
|
||||
<div id="scoreList" class="flex-1 overflow-y-auto max-h-[500px] bg-white rounded-lg shadow p-4 space-y-3">
|
||||
<!-- Scores will be dynamically inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-8">
|
||||
<button id="newAnalysisBtn" class="btn-hover bg-green-600 hover:bg-green-700 text-white font-medium py-3 px-8 rounded-lg transition-all transform hover:scale-105">
|
||||
Start New Analysis <i class="fas fa-plus ml-2"></i>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// All JavaScript logic is contained here.
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// ================== DOM Elements ==================
|
||||
const uploadBtn = document.getElementById('uploadBtn');
|
||||
const cameraBtn = document.getElementById('cameraBtn');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const initialView = document.getElementById('initialView');
|
||||
const cameraView = document.getElementById('cameraView');
|
||||
const cameraPreview = document.getElementById('cameraPreview');
|
||||
const captureBtn = document.getElementById('captureBtn');
|
||||
const previewView = document.getElementById('previewView');
|
||||
const previewImage = document.getElementById('previewImage');
|
||||
const hiddenCanvas = document.getElementById('hiddenCanvas');
|
||||
const retakeBtn = document.getElementById('retakeBtn');
|
||||
const submitContainer = document.getElementById('submitContainer');
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
const loadingIndicator = document.getElementById('loadingIndicator');
|
||||
const errorMsg = document.getElementById('errorMsg');
|
||||
const resultsSection = document.getElementById('resultsSection');
|
||||
const resultImage = document.getElementById('resultImage');
|
||||
const maskImage = document.getElementById('maskImage');
|
||||
const scoreList = document.getElementById('scoreList');
|
||||
const newAnalysisBtn = document.getElementById('newAnalysisBtn');
|
||||
|
||||
// ================== State & Configuration ==================
|
||||
let currentFile = null;
|
||||
let cameraStream = null;
|
||||
const clientId = 'YTiFKSfOADhKE6HOS7wPbcrxeXA1LUyF';
|
||||
const publicKey = `-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCGdjQUhil+XNmLtjLcsAAl+tV2bg0mEZAmkr7rg4Yn13wWoM4NY/uJk1ao0sN9ZfHmp7E+53fZYwPeD4wPss7q97h+MguhtDxdJMWarlora11adFpRN+VLEYCzfOPHOR2odrQdhQ5t9DtT0KyNJ4B2vcBzVwVslMk+AoSt5AOrUwIDAQAB
|
||||
-----END PUBLIC KEY-----`;
|
||||
|
||||
// ================== Helper Functions ==================
|
||||
function setView(viewId) {
|
||||
const views = ['initialView', 'cameraView', 'previewView', 'resultsSection', 'submitContainer'];
|
||||
views.forEach(id => {
|
||||
const element = document.getElementById(id);
|
||||
if (id === viewId || (viewId === 'previewView' && id === 'submitContainer') || (viewId === 'resultsSection' && id === 'submitContainer')) {
|
||||
element.classList.remove('hidden');
|
||||
} else {
|
||||
element.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
if (viewId === 'resultsSection') {
|
||||
document.getElementById('appContainer').classList.add('hidden');
|
||||
resultsSection.classList.remove('hidden');
|
||||
} else {
|
||||
document.getElementById('appContainer').classList.remove('hidden');
|
||||
resultsSection.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// --- API & Core Logic Functions ---
|
||||
async function authenticate() {
|
||||
const plain = `client_id=${clientId}×tamp=${Date.now()}`;
|
||||
const encrypt = new JSEncrypt();
|
||||
encrypt.setPublicKey(publicKey);
|
||||
const id_token = encrypt.encrypt(plain);
|
||||
if (!id_token) throw new Error('Encryption failed');
|
||||
const response = await fetch('https://yce-api-01.perfectcorp.com/s2s/v1.0/client/auth', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ client_id: clientId, id_token })
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(`Authentication failed: ${error.error || response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
return data.result.access_token;
|
||||
}
|
||||
|
||||
async function createUploadSlot(token, fileName, fileSize, contentType = 'image/jpeg') {
|
||||
const response = await fetch('https://yce-api-01.perfectcorp.com/s2s/v1.1/file/skin-analysis', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
files: [{
|
||||
content_type: contentType,
|
||||
file_name: fileName,
|
||||
file_size: fileSize
|
||||
}]
|
||||
})
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errText = await response.text();
|
||||
throw new Error('Failed to create file slot: ' + errText);
|
||||
}
|
||||
const data = await response.json();
|
||||
return data.result.files[0];
|
||||
}
|
||||
|
||||
async function uploadToS3(uploadRequest, file) {
|
||||
const response = await fetch(uploadRequest.url, {
|
||||
method: uploadRequest.method,
|
||||
headers: uploadRequest.headers,
|
||||
body: file
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`S3 upload failed with status ${response.status}`);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async function analyzeskin(token, fileId) {
|
||||
const payload = {
|
||||
request_id: 0,
|
||||
payload: {
|
||||
file_sets: {
|
||||
src_ids: [fileId.trim()]
|
||||
},
|
||||
actions: [{
|
||||
id: 0,
|
||||
params: {},
|
||||
dst_actions: ["wrinkle", "pore", "texture", "acne"]
|
||||
}]
|
||||
}
|
||||
};
|
||||
const response = await fetch('https://yce-api-01.perfectcorp.com/s2s/v1.0/task/skin-analysis', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorDetails = await response.json();
|
||||
throw new Error(`Analysis failed: ${errorDetails.error || response.statusText}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (!result.result || !result.result.task_id) {
|
||||
throw new Error("No task_id returned from API");
|
||||
}
|
||||
return result.result.task_id;
|
||||
}
|
||||
|
||||
async function getAnalysisResult(token, taskId) {
|
||||
const response = await fetch(
|
||||
`https://yce-api-01.perfectcorp.com/s2s/v1.0/task/skin-analysis?task_id=${encodeURIComponent(taskId)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
}
|
||||
);
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(`Failed to get results: ${error.error || response.statusText}`);
|
||||
}
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async function pollForAnalysisResult(token, taskId) {
|
||||
while (true) {
|
||||
try {
|
||||
const result = await getAnalysisResult(token, taskId);
|
||||
const status = result.result.status;
|
||||
const pollingInterval = result.result.polling_interval || 2000;
|
||||
if (status === 'success') {
|
||||
if (result.result.results && result.result.results.length > 0) {
|
||||
return result;
|
||||
}
|
||||
throw new Error('Empty results array');
|
||||
}
|
||||
if (status === 'error') {
|
||||
const errorMessage = result.result.error_message || 'No detailed error message provided';
|
||||
throw new Error(`Analysis error: ${errorMessage}`);
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, pollingInterval));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function resizeImageFile(file, maxWidth = 2048, maxHeight = 2048, minShortSide = 1080) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
let { width, height } = img;
|
||||
let scaleRatio = 1;
|
||||
if (width > maxWidth || height > maxHeight) {
|
||||
const widthRatio = maxWidth / width;
|
||||
const heightRatio = maxHeight / height;
|
||||
scaleRatio = Math.min(widthRatio, heightRatio);
|
||||
}
|
||||
const shortSide = Math.min(width, height);
|
||||
if (shortSide * scaleRatio < minShortSide) {
|
||||
const upscaleRatio = minShortSide / shortSide;
|
||||
scaleRatio = Math.max(upscaleRatio, scaleRatio);
|
||||
}
|
||||
const targetWidth = Math.round(width * scaleRatio);
|
||||
const targetHeight = Math.round(height * scaleRatio);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = targetWidth;
|
||||
canvas.height = targetHeight;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
|
||||
canvas.toBlob(
|
||||
(blob) => {
|
||||
if (!blob) return reject(new Error('Canvas is empty'));
|
||||
const resizedFile = new File([blob], file.name.replace(/\.\w+$/, '.jpg'), { type: 'image/jpeg' });
|
||||
resolve(resizedFile);
|
||||
},
|
||||
'image/jpeg',
|
||||
0.9
|
||||
);
|
||||
};
|
||||
img.onerror = () => reject(new Error('Failed to load image'));
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
img.src = e.target.result;
|
||||
};
|
||||
reader.onerror = () => reject(new Error('File reading error'));
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
// ================== Event Listeners ==================
|
||||
uploadBtn.addEventListener('click', () => fileInput.click());
|
||||
|
||||
fileInput.addEventListener('change', (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
currentFile = file;
|
||||
previewImage.src = URL.createObjectURL(file);
|
||||
setView('previewView');
|
||||
}
|
||||
});
|
||||
|
||||
cameraBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
cameraStream = await navigator.mediaDevices.getUserMedia({
|
||||
video: { facingMode: 'user' },
|
||||
audio: false
|
||||
});
|
||||
cameraPreview.srcObject = cameraStream;
|
||||
cameraPreview.play();
|
||||
setView('cameraView');
|
||||
} catch (err) {
|
||||
errorMsg.textContent = "Could not access the camera. Please check permissions.";
|
||||
errorMsg.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
captureBtn.addEventListener('click', () => {
|
||||
if (!cameraStream) return;
|
||||
hiddenCanvas.width = cameraPreview.videoWidth;
|
||||
hiddenCanvas.height = cameraPreview.videoHeight;
|
||||
const ctx = hiddenCanvas.getContext('2d');
|
||||
ctx.drawImage(cameraPreview, 0, 0, hiddenCanvas.width, hiddenCanvas.height);
|
||||
hiddenCanvas.toBlob((blob) => {
|
||||
if (blob) {
|
||||
currentFile = new File([blob], "captured-image.jpeg", { type: "image/jpeg" });
|
||||
previewImage.src = URL.createObjectURL(currentFile);
|
||||
setView('previewView');
|
||||
}
|
||||
}, 'image/jpeg', 0.9);
|
||||
cameraStream.getTracks().forEach(track => track.stop());
|
||||
cameraStream = null;
|
||||
});
|
||||
|
||||
retakeBtn.addEventListener('click', () => {
|
||||
currentFile = null;
|
||||
previewImage.src = '';
|
||||
setView('initialView');
|
||||
errorMsg.classList.add('hidden');
|
||||
});
|
||||
|
||||
newAnalysisBtn.addEventListener('click', () => {
|
||||
currentFile = null;
|
||||
previewImage.src = '';
|
||||
resultImage.src = '';
|
||||
maskImage.src = '';
|
||||
scoreList.innerHTML = '';
|
||||
setView('initialView');
|
||||
errorMsg.classList.add('hidden');
|
||||
});
|
||||
|
||||
submitBtn.addEventListener('click', async () => {
|
||||
if (!currentFile) {
|
||||
errorMsg.textContent = 'Please select or capture a photo first.';
|
||||
errorMsg.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Processing...';
|
||||
loadingIndicator.classList.remove('hidden');
|
||||
errorMsg.classList.add('hidden');
|
||||
|
||||
try {
|
||||
const resizedFile = await resizeImageFile(currentFile);
|
||||
const token = await authenticate();
|
||||
const uploadSlot = await createUploadSlot(token, resizedFile.name, resizedFile.size);
|
||||
await uploadToS3(uploadSlot.requests[0], resizedFile);
|
||||
const cleanFileId = uploadSlot.file_id.replace(/^\/+/,'');
|
||||
const taskId = await analyzeskin(token, cleanFileId);
|
||||
const result = await pollForAnalysisResult(token, taskId);
|
||||
|
||||
const scores = result.result.results[0].scores;
|
||||
const maskZipUrl = result.result.results[0].data[0].url;
|
||||
|
||||
const zipBlob = await fetch(maskZipUrl).then(r => r.blob());
|
||||
const zip = await JSZip.loadAsync(zipBlob);
|
||||
|
||||
const maskMap = {};
|
||||
for (const key in scores) {
|
||||
const score = scores[key];
|
||||
if (score.output_mask_name && zip.file(score.output_mask_name)) {
|
||||
const fileData = await zip.file(score.output_mask_name).async("blob");
|
||||
maskMap[score.output_mask_name] = URL.createObjectURL(fileData);
|
||||
}
|
||||
}
|
||||
|
||||
// Display results
|
||||
resultImage.src = URL.createObjectURL(currentFile);
|
||||
scoreList.innerHTML = '';
|
||||
|
||||
Object.keys(scores).filter(k => scores[k].ui_score !== undefined).forEach(metric => {
|
||||
const score = scores[metric];
|
||||
const card = document.createElement('div');
|
||||
card.className = 'score-card p-4 rounded-lg cursor-pointer transition-all duration-200 bg-gray-50 hover:bg-gray-100';
|
||||
card.innerHTML = `<span class="capitalize">${metric.replace(/_/g, ' ')}</span><span class="font-bold text-blue-700 ml-2">${score.ui_score}</span>`;
|
||||
|
||||
card.addEventListener('mouseenter', () => {
|
||||
if (score.output_mask_name && maskMap[score.output_mask_name]) {
|
||||
maskImage.src = maskMap[score.output_mask_name];
|
||||
maskImage.classList.remove('hidden');
|
||||
}
|
||||
card.classList.add('bg-blue-100', 'border-l-4', 'border-blue-500');
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', () => {
|
||||
maskImage.classList.add('hidden');
|
||||
card.classList.remove('bg-blue-100', 'border-l-4', 'border-blue-500');
|
||||
});
|
||||
|
||||
scoreList.appendChild(card);
|
||||
});
|
||||
|
||||
setView('resultsSection');
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
errorMsg.textContent = `Analysis failed: ${e.message}`;
|
||||
errorMsg.classList.remove('hidden');
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Analyze My skin';
|
||||
loadingIndicator.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Add animation to elements when they come into view
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('fade-in');
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.1 });
|
||||
document.querySelectorAll('section, .feature-card').forEach(section => {
|
||||
observer.observe(section);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1 @@
|
||||
/*! @license DOMPurify 3.2.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.6/LICENSE */
|
||||
@@ -0,0 +1,411 @@
|
||||
/*!
|
||||
|
||||
JSZip v3.10.1 - A JavaScript class for generating and reading zip files
|
||||
<http://stuartk.com/jszip>
|
||||
|
||||
(c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
|
||||
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.
|
||||
|
||||
JSZip uses the library pako released under the MIT license :
|
||||
https://github.com/nodeca/pako/blob/main/LICENSE
|
||||
*/
|
||||
|
||||
/*!
|
||||
* html2canvas 1.4.1 <https://html2canvas.hertzen.com>
|
||||
* Copyright (c) 2022 Niklas von Hertzen <https://hertzen.com>
|
||||
* Released under MIT License
|
||||
*/
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
/**
|
||||
* @license
|
||||
Copyright (c) 2008, Adobe Systems Incorporated
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Adobe Systems Incorporated nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
*
|
||||
* Copyright (c) 2014 James Robb, https://github.com/jamesbrobb
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
*
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* (c) Dean McNamee <dean@gmail.com>, 2013.
|
||||
*
|
||||
* https://github.com/deanm/omggif
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
* omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
|
||||
* including animation and compression. It does not rely on any specific
|
||||
* underlying system, so should run in the browser, Node, or Plask.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv
|
||||
*
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2017 Aras Abbasi
|
||||
*
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2018 Aras Abbasi
|
||||
*
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2019 Aras Abbasi
|
||||
*
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* =============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* FPDF is released under a permissive license: there is no usage restriction.
|
||||
* You may embed it freely in your application (commercial or not), with or
|
||||
* without modifications.
|
||||
*
|
||||
* Reference: http://www.fpdf.org/en/script/script37.php
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Joseph Myers does not specify a particular license for his work.
|
||||
*
|
||||
* Author: Joseph Myers
|
||||
* Accessed from: http://www.myersdaily.org/joseph/javascript/md5.js
|
||||
*
|
||||
* Modified by: Owen Leong
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
* Author: Owen Leong (@owenl131)
|
||||
* Date: 15 Oct 2020
|
||||
* References:
|
||||
* https://www.cs.cmu.edu/~dst/Adobe/Gallery/anon21jul01-pdf-encryption.txt
|
||||
* https://github.com/foliojs/pdfkit/blob/master/lib/security.js
|
||||
* http://www.fpdf.org/en/script/script37.php
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Unicode Bidi Engine based on the work of Alex Shensis (@asthensis)
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* jsPDF fileloading PlugIn
|
||||
* Copyright (c) 2018 Aras Abbasi (aras.abbasi@gmail.com)
|
||||
*
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* jsPDF filters PlugIn
|
||||
* Copyright (c) 2014 Aras Abbasi
|
||||
*
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* jsPDF virtual FileSystem functionality
|
||||
*
|
||||
* Licensed under the MIT License.
|
||||
* http://opensource.org/licenses/mit-license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom-client.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-jsx-runtime.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* scheduler.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class to parse color values
|
||||
* @author Stoyan Stefanov <sstoo@gmail.com>
|
||||
* {@link http://www.phpied.com/rgb-color-parser-in-javascript/}
|
||||
* @license Use it if you like it
|
||||
*/
|
||||
|
||||
/**
|
||||
* react-router v7.9.1
|
||||
*
|
||||
* Copyright (c) Remix Software Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/** ====================================================================
|
||||
* @license
|
||||
* jsPDF XMP metadata plugin
|
||||
* Copyright (c) 2016 Jussi Utunen, u-jussi@suomi24.fi
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
/** @license
|
||||
* Copyright (c) 2017 Dominik Homberger
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
https://webpjs.appspot.com
|
||||
WebPRiffParser dominikhlbg@gmail.com
|
||||
*/
|
||||
|
||||
/** @license
|
||||
*
|
||||
* jsPDF - PDF Document creation from JavaScript
|
||||
* Version 3.0.1 Built on 2025-03-17T14:19:36.873Z
|
||||
* CommitID 00000000
|
||||
*
|
||||
* Copyright (c) 2010-2021 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF
|
||||
* 2015-2021 yWorks GmbH, http://www.yworks.com
|
||||
* 2015-2021 Lukas Holländer <lukas.hollaender@yworks.com>, https://github.com/HackbrettXXX
|
||||
* 2016-2018 Aras Abbasi <aras.abbasi@gmail.com>
|
||||
* 2010 Aaron Spike, https://github.com/acspike
|
||||
* 2012 Willow Systems Corporation, https://github.com/willowsystems
|
||||
* 2012 Pablo Hess, https://github.com/pablohess
|
||||
* 2012 Florian Jenett, https://github.com/fjenett
|
||||
* 2013 Warren Weckesser, https://github.com/warrenweckesser
|
||||
* 2013 Youssef Beddad, https://github.com/lifof
|
||||
* 2013 Lee Driscoll, https://github.com/lsdriscoll
|
||||
* 2013 Stefan Slonevskiy, https://github.com/stefslon
|
||||
* 2013 Jeremy Morel, https://github.com/jmorel
|
||||
* 2013 Christoph Hartmann, https://github.com/chris-rock
|
||||
* 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
|
||||
* 2014 James Makes, https://github.com/dollaruw
|
||||
* 2014 Diego Casorran, https://github.com/diegocr
|
||||
* 2014 Steven Spungin, https://github.com/Flamenco
|
||||
* 2014 Kenneth Glassey, https://github.com/Gavvers
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Contributor(s):
|
||||
* siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango,
|
||||
* kim3er, mfo, alnorth, Flamenco
|
||||
*/
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "/elixifyskinanalyser",
|
||||
"dependencies": {
|
||||
"@fontsource/albert-sans": "^5.2.8",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^6.6.4",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"face-api.js": "^0.22.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-encrypt": "^2.3.4",
|
||||
"jsencrypt": "^3.5.4",
|
||||
"jspdf": "^3.0.1",
|
||||
"jszip": "^3.10.1",
|
||||
"postcss": "^8.5.6",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-image-file-resizer": "^0.4.8",
|
||||
"react-router": "^7.9.1",
|
||||
"react-router-dom": "^7.9.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"prebuild": "node -e \"const fs=require('fs'); fs.writeFileSync('src/build-info.json', JSON.stringify({version: Date.now(), date: new Date().toISOString()}))\"",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"http-proxy-middleware": "^3.0.5",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"ts-interface-checker": "^1.0.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"age_spot": {
|
||||
"raw_score": 98.26628875732422,
|
||||
"ui_score": 96,
|
||||
"output_mask_name": "age_spot_output.png"
|
||||
},
|
||||
"radiance": {
|
||||
"raw_score": 97.64312505722046,
|
||||
"ui_score": 96,
|
||||
"output_mask_name": "radiance_output.png"
|
||||
},
|
||||
"oiliness": {
|
||||
"raw_score": 83.94287872314453,
|
||||
"ui_score": 82,
|
||||
"output_mask_name": "oiliness_output.png"
|
||||
},
|
||||
"firmness": {
|
||||
"raw_score": 89.18848633766174,
|
||||
"ui_score": 85,
|
||||
"output_mask_name": "firmness_output.png"
|
||||
},
|
||||
"dark_circle_v2": {
|
||||
"raw_score": 55.23899793624878,
|
||||
"ui_score": 67,
|
||||
"output_mask_name": "dark_circle_v2_output.png"
|
||||
},
|
||||
"droopy_lower_eyelid": {
|
||||
"raw_score": 80.4837703704834,
|
||||
"ui_score": 81,
|
||||
"output_mask_name": "droopy_lower_eyelid_output.png"
|
||||
},
|
||||
"droopy_upper_eyelid": {
|
||||
"raw_score": 82.7034592628479,
|
||||
"ui_score": 82,
|
||||
"output_mask_name": "droopy_upper_eyelid_output.png"
|
||||
},
|
||||
"wrinkle": {
|
||||
"raw_score": 95.83742618560791,
|
||||
"ui_score": 91,
|
||||
"output_mask_name": "wrinkle_output.png"
|
||||
},
|
||||
"redness": {
|
||||
"raw_score": 100.0,
|
||||
"ui_score": 99,
|
||||
"output_mask_name": "redness_output.png"
|
||||
},
|
||||
"pore": {
|
||||
"raw_score": 99.96079206466675,
|
||||
"ui_score": 99,
|
||||
"output_mask_name": "pore_output.png"
|
||||
},
|
||||
"eye_bag": {
|
||||
"raw_score": 88.28426599502563,
|
||||
"ui_score": 84,
|
||||
"output_mask_name": "eye_bag_output.png"
|
||||
},
|
||||
"texture": {
|
||||
"raw_score": 97.28496074676514,
|
||||
"ui_score": 94,
|
||||
"output_mask_name": "texture_output.png"
|
||||
},
|
||||
"moisture": {
|
||||
"raw_score": 71.30312919616699,
|
||||
"ui_score": 78,
|
||||
"output_mask_name": "moisture_output.png"
|
||||
},
|
||||
"acne": {
|
||||
"raw_score": 100.0,
|
||||
"ui_score": 99,
|
||||
"output_mask_name": "acne_output.png"
|
||||
},
|
||||
"all": {
|
||||
"score": 88.07142857142857
|
||||
},
|
||||
"skin_age": 26
|
||||
}
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 294 KiB |
|
After Width: | Height: | Size: 398 KiB |
|
After Width: | Height: | Size: 408 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 765 B |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 54 KiB |