├── requirements.txt ├── app ├── models │ └── models.md ├── static │ ├── style.css │ └── client.js ├── view │ └── index.html └── server.py ├── README.md └── Dockerfile /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | torchvision 3 | https://download.pytorch.org/whl/cpu/torch-1.0.1.post2-cp37-cp37m-linux_x86_64.whl 4 | fastai 5 | starlette 6 | uvicorn==0.3.32 7 | python-multipart 8 | aiofiles 9 | aiohttp 10 | -------------------------------------------------------------------------------- /app/models/models.md: -------------------------------------------------------------------------------- 1 | * The .pth files should get downloaded here. 2 | * Don't put your models inside this folder beforehand as these files can be large in size, and we want to keep our cloud deployment as light as possible and ask the app to download whatever file it needs. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Age-Detector-Web-App 2 | A simple web app for age detection based on a [Hackathon problem](https://datahack.analyticsvidhya.com/contest/practice-problem-age-detection/) by Analytics Vidhya 3 | 4 | Demo: 5 | https://www.loom.com/share/587a722d8a644ceeb80748a28356109f 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-slim-stretch 2 | 3 | RUN apt-get update && apt-get install -y git python3-dev gcc \ 4 | && rm -rf /var/lib/apt/lists/* 5 | 6 | COPY requirements.txt . 7 | 8 | RUN pip install --upgrade pip 9 | 10 | RUN pip install --no-cache-dir -r requirements.txt --upgrade 11 | 12 | COPY app app/ 13 | 14 | RUN python app/server.py 15 | 16 | EXPOSE 5042 17 | 18 | CMD ["python", "app/server.py", "serve"] -------------------------------------------------------------------------------- /app/static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #fff; 3 | } 4 | 5 | .no-display { 6 | display: none; 7 | } 8 | 9 | .center { 10 | margin: auto; 11 | padding: 10px; 12 | padding-left:50px; 13 | padding-right:50px; 14 | text-align: center; 15 | font-size: 14px; 16 | } 17 | 18 | .title { 19 | font-size: 30px; 20 | margin-top: 1em; 21 | margin-bottom: 1em; 22 | color: #262626; 23 | } 24 | 25 | .content { 26 | margin-top: 10em; 27 | } 28 | 29 | .analyze { 30 | margin-top: 5em; 31 | } 32 | 33 | .upload-label { 34 | padding: 10px; 35 | font-size: 12px; 36 | } 37 | 38 | .result-label { 39 | margin-top: 0.5em; 40 | padding: 10px; 41 | font-size: 13px; 42 | } 43 | 44 | button.choose-file-button { 45 | width: 200px; 46 | height: 40px; 47 | border-radius: 2px; 48 | background-color: #ffffff; 49 | border: solid 1px #7052CB; 50 | font-size: 13px; 51 | color: #7052CB; 52 | } 53 | 54 | button.analyze-button { 55 | width: 200px; 56 | height: 40px; 57 | border: solid 1px #7052CB; 58 | border-radius: 2px; 59 | background-color: #7052CB; 60 | font-size: 13px; 61 | color: #ffffff; 62 | } 63 | 64 | button:focus {outline:0;} -------------------------------------------------------------------------------- /app/static/client.js: -------------------------------------------------------------------------------- 1 | var el = x => document.getElementById(x); 2 | 3 | function showPicker(inputId) { el('file-input').click(); } 4 | 5 | function showPicked(input) { 6 | el('upload-label').innerHTML = input.files[0].name; 7 | var reader = new FileReader(); 8 | reader.onload = function (e) { 9 | el('image-picked').src = e.target.result; 10 | el('image-picked').className = ''; 11 | } 12 | reader.readAsDataURL(input.files[0]); 13 | } 14 | 15 | function analyze() { 16 | var uploadFiles = el('file-input').files; 17 | if (uploadFiles.length != 1) alert('Please select 1 file to analyze!'); 18 | 19 | el('analyze-button').innerHTML = 'Analyzing...'; 20 | var xhr = new XMLHttpRequest(); 21 | var loc = window.location 22 | xhr.open('POST', `${loc.protocol}//${loc.hostname}:${loc.port}/analyze`, true); 23 | xhr.onerror = function() {alert (xhr.responseText);} 24 | xhr.onload = function(e) { 25 | if (this.readyState === 4) { 26 | var response = JSON.parse(e.target.responseText); 27 | el('result-label').innerHTML = `Result = ${response['result']}`; 28 | } 29 | el('analyze-button').innerHTML = 'Analyze'; 30 | } 31 | 32 | var fileData = new FormData(); 33 | fileData.append('file', uploadFiles[0]); 34 | xhr.send(fileData); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /app/view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
Detect age from a face
11 |

12 | Try using front-facing images of size 128*128 13 |

14 |
15 |
16 | 22 |
23 | 24 |
25 | 26 |
27 |
28 | Chosen Image 29 |
30 |
31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /app/server.py: -------------------------------------------------------------------------------- 1 | from starlette.applications import Starlette 2 | from starlette.responses import HTMLResponse, JSONResponse 3 | from starlette.staticfiles import StaticFiles 4 | from starlette.middleware.cors import CORSMiddleware 5 | import uvicorn, aiohttp, asyncio 6 | from io import BytesIO 7 | 8 | from fastai import * 9 | from fastai.vision import * 10 | 11 | # export_file_url = 'https://www.dropbox.com/s/v6cuuvddq73d1e0/export.pkl?raw=1' 12 | export_file_url = 'https://drive.google.com/uc?export=download&id=1yuKjHpuWGDBG9C8zUMVmvvdb7BXQ3xSL' 13 | export_file_name = 'age-model.pkl' 14 | 15 | classes = ['MIDDLE', 'OLD', 'YOUNG'] 16 | path = Path(__file__).parent 17 | 18 | app = Starlette() 19 | app.add_middleware(CORSMiddleware, allow_origins=['*'], allow_headers=['X-Requested-With', 'Content-Type']) 20 | app.mount('/static', StaticFiles(directory='app/static')) 21 | 22 | async def download_file(url, dest): 23 | if dest.exists(): return 24 | async with aiohttp.ClientSession() as session: 25 | async with session.get(url) as response: 26 | data = await response.read() 27 | with open(dest, 'wb') as f: f.write(data) 28 | 29 | async def setup_learner(): 30 | await download_file(export_file_url, path/export_file_name) 31 | try: 32 | learn = load_learner(path, export_file_name) 33 | return learn 34 | except RuntimeError as e: 35 | if len(e.args) > 0 and 'CPU-only machine' in e.args[0]: 36 | print(e) 37 | message = "\n\nThis model was trained with an old version of fastai and will not work in a CPU environment.\n\nPlease update the fastai library in your training environment and export your model again.\n\nSee instructions for 'Returning to work' at https://course.fast.ai." 38 | raise RuntimeError(message) 39 | else: 40 | raise 41 | 42 | loop = asyncio.get_event_loop() 43 | tasks = [asyncio.ensure_future(setup_learner())] 44 | learn = loop.run_until_complete(asyncio.gather(*tasks))[0] 45 | loop.close() 46 | 47 | @app.route('/') 48 | def index(request): 49 | html = path/'view'/'index.html' 50 | return HTMLResponse(html.open().read()) 51 | 52 | @app.route('/analyze', methods=['POST']) 53 | async def analyze(request): 54 | data = await request.form() 55 | img_bytes = await (data['file'].read()) 56 | img = open_image(BytesIO(img_bytes)) 57 | prediction = learn.predict(img)[0] 58 | return JSONResponse({'result': str(prediction)}) 59 | 60 | if __name__ == '__main__': 61 | if 'serve' in sys.argv: uvicorn.run(app=app, host='0.0.0.0', port=5042) 62 | --------------------------------------------------------------------------------