Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
harryjmoss committed Jun 18, 2019
0 parents commit 3e7dfa7
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 0 deletions.
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM python:3.6-slim-stretch

RUN apt update
RUN apt install -y python3-dev gcc

# Install pytorch and fastai
RUN pip install torch_nightly -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html
RUN pip install fastai

# Install starlette and uvicorn
RUN pip install starlette uvicorn python-multipart aiohttp

ADD imgapp.py imgapp.py
ADD resnet50_EAP_version6.pkl resnet50_EAP_version6.pkl

# Run it once to trigger resnet download
RUN python imgapp.py

EXPOSE 8008

# Start the server
CMD ["python", "imgapp.py", "serve"]
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Image orientation detection
*App inspired by the work of [Deepanshu Thakur](https://github.com/Deepanshu2017/AlligatorOrCrocodile) and [Simon Willison](https://github.com/simonw/cougar-or-not)*

This webapp was created on behalf of the British Library [Endangered Archive Programme](https://eap.bl.uk/) to determine the orientation of images in their collection. The deep learning model is based on a 50-layer version of [resnet](https://arxiv.org/abs/1512.03385) and was trained on around 650 unique images from the EAP archives using the [fast.ai](https://docs.fast.ai/) library. Each image is duplicated into four representations, consisting of the application of 0, 90, 180 or 270 degrees of anti-clockwise rotation.

The 99MB pre-trained model is included in this package and is found within `resnet50_EAP_version6.pkl`

`imgapp.py` is a [Starlette](https://www.starlette.io/) API server which accepts image uploads or image URLs and runs them against the pre-calculated model.

A `Dockerfile` is included for convenience.

99 changes: 99 additions & 0 deletions imgapp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from starlette.applications import Starlette
from starlette.responses import JSONResponse, HTMLResponse, RedirectResponse
from fastai import *
from fastai.vision import *

import torch
from pathlib import Path
from io import BytesIO
import sys
import uvicorn
import aiohttp
import asyncio


async def get_bytes(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.read()


app = Starlette()
path = Path(".")
preTrainedModel="resnet50_EAP_version6.pkl"
classes = ["0","90","180","270"]





data = ImageDataBunch.single_from_classes(
path,
classes,
ds_tfms=get_transforms(),
size=224,
).normalize(imagenet_stats)

learn=load_learner(path,preTrainedModel)

@app.route("/upload", methods=["POST"])
async def upload(request):
data = await request.form()
bytes = await (data["file"].read())
return predict_image_from_bytes(bytes)


@app.route("/classify-url", methods=["GET"])
async def classify_url(request):
bytes = await get_bytes(request.query_params["url"])
return predict_image_from_bytes(bytes)


def predict_image_from_bytes(bytes):
"""
Predict function called by either classify_url or by upload
Returns True class as well as the scores
Scores are *not* probabilities - use activation function (softmax, sigmoid, etc)
to convert to probabilities!
"""

img = open_image(BytesIO(bytes))
class_, predictions, losses = learn.predict(img)
return JSONResponse({
"class": class_,
"scores": sorted(
zip(learn.data.classes, map(float, losses)),
key=lambda p: p[1],
reverse=True
)
})


@app.route("/")
def form(request):
return HTMLResponse(
"""
<h3> Image orientation prediction app <h3>
<h4> Trained on approximately 650 images, each with four representations<h4>
<form action="/upload" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="file">
<input type="submit" value="Upload Image">
</form>
Or submit a URL:
<form action="/classify-url" method="get">
<input type="url" name="url">
<input type="submit" value="Fetch and analyze image">
</form>
""")


@app.route("/form")
def redirect_to_homepage(request):
return RedirectResponse("/")


if __name__ == "__main__":
if "serve" in sys.argv:
uvicorn.run(app, host="0.0.0.0", port=8080)
Binary file added resnet50_EAP_version6.pkl
Binary file not shown.

0 comments on commit 3e7dfa7

Please sign in to comment.