This repository has been archived by the owner on Dec 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
178 lines (145 loc) · 4.75 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import io
import logging
from mimetypes import guess_extension
import tempfile
import uuid
from autocrop.autocrop import Cropper
import cv2
from google.cloud import storage
import google.cloud.logging
import magic
import numpy as np
from PIL import Image
import requests
from fastapi import FastAPI, File, UploadFile
from fastapi_versioning import VersionedFastAPI, version
from fastapi.logger import logger as fastapi_logger
from starlette import status
from starlette.responses import Response, FileResponse
from starlette.middleware.cors import CORSMiddleware
from pydantic import AnyHttpUrl
app = FastAPI()
# Google Cloud Storage
BUCKET = "autocrop-img"
storage_client = storage.Client() # Implicitly reads environment variable
bucket = storage_client.bucket(BUCKET)
# Logging
# Instantiates a client
client = google.cloud.logging.Client()
# Connects the logger to the root logging handler
client.setup_logging()
def open_file(file):
"""Given a filename, returns a numpy array"""
try:
# Try with PIL
with Image.open(file) as img_orig:
return np.asarray(img_orig)
except Exception:
fastapi_logger.warn(f"PIL unable to open {file}, trying cv2.")
# Try with cv2
# TODO: this might not be working
return cv2.imread(file)
return None, None
def upload_blob(img, ext: str, mime: str):
"""Given an img array and extension, uploads it to GStorage."""
if "." in ext:
ext = ext[1:]
filename = str(uuid.uuid4()) + "." + ext
fastapi_logger.info(f"Uploading to Storage: {filename}")
blob = bucket.blob(filename)
with tempfile.NamedTemporaryFile(suffix=ext) as temp:
temp_filename = temp.name + "." + ext
cv2.imwrite(temp_filename, img)
blob.upload_from_filename(temp_filename, content_type=mime)
blob.make_public()
return blob.public_url
async def upload_img_file(img, ext: str, mime: str):
"""Given an img file, returns a temp file to user."""
if "." in ext:
ext = ext[1:]
with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as FOUT:
FOUT.write(img)
return FileResponse(FOUT.name, media_type=mime)
def get_mime(file):
"""Given a file, returns mimetype and extension"""
mime = magic.from_buffer(file.read(2048), mime=True)
extension = guess_extension(mime, False)
return mime, extension
@app.post("/crop")
@version(1)
async def crop(
width: int = 500,
height: int = 500,
face_percent: int = 50,
file: UploadFile = File(...),
):
"""Returns a cropped form of the document image."""
mime, extension = get_mime(file.file)
fastapi_logger.info(f"Reading file: {file.filename}, mime: {mime}")
if "image" not in mime:
return Response(status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE)
# Set up Cropper instance and crop
c = Cropper(width=width, height=height, face_percent=face_percent)
# Crop
img = open_file(file.file)
img_array = c.crop(img)
if img_array is None:
return {"success": False, "description": "No face detected", "url": None}
url = upload_blob(img=img_array, ext=extension, mime=mime)
return {
"success": True,
"description": "Face detected, cropped at url provided.",
"url": url,
}
@app.post("/crop_uri")
@version(1)
async def crop_uri(
uri: AnyHttpUrl, width: int = 500, height: int = 500, face_percent: int = 50,
):
"""Returns a cropped form of the document image."""
# Set up Cropper instance and crop
c = Cropper(width=width, height=height, face_percent=face_percent)
# Crop
r = requests.get(uri, stream=True)
if r.status_code != 200:
# TODO
return None
file = io.BytesIO(r.content)
mime, extension = get_mime(file)
fastapi_logger.info(f"Reading file: {mime}, {extension}")
img = open_file(file)
img_array = c.crop(img)
if img_array is None:
# TODO
return {"face detected": None}
# Convert to bytes
is_success, img_buffer = cv2.imencode(extension, img_array)
if not is_success:
# TODO
return {"face detected": None}
byte_im = img_buffer.tobytes()
return await upload_img_file(img=byte_im, ext=extension, mime=mime)
@app.get("/*")
def home():
return {
"autocrop API": {
"version": "1",
"source": "https://github.com/leblancfg/autocrop",
"docs": "$URL/v1/docs",
}
}
@app.get("/healthcheck")
async def healthcheck():
return {"status": "alive"}
app = VersionedFastAPI(app, version_format="{major}", prefix_format="/v{major}",)
origins = [
"http://localhost",
"http://localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)