diff options
| -rw-r--r-- | client/common/upload.helpers.js | 6 | ||||
| -rw-r--r-- | client/common/uploadImage.component.js | 3 | ||||
| -rw-r--r-- | client/faceAnalysis/faceAnalysis.actions.js | 39 | ||||
| -rw-r--r-- | client/faceAnalysis/faceAnalysis.query.js | 10 | ||||
| -rw-r--r-- | client/faceAnalysis/faceAnalysis.reducer.js | 1 | ||||
| -rw-r--r-- | client/faceAnalysis/faceAnalysis.result.js | 54 | ||||
| -rw-r--r-- | megapixels/app/server/api_task.py | 48 | ||||
| -rw-r--r-- | megapixels/app/server/tasks/blur.py | 27 | ||||
| -rw-r--r-- | package-lock.json | 29 | ||||
| -rw-r--r-- | package.json | 1 |
10 files changed, 81 insertions, 137 deletions
diff --git a/client/common/upload.helpers.js b/client/common/upload.helpers.js index 5a041fd4..eb42a993 100644 --- a/client/common/upload.helpers.js +++ b/client/common/upload.helpers.js @@ -15,15 +15,15 @@ function base64ToUint8Array(string, start, finish) { } function getOrientation(uri) { - const exif = new ExifReader() // Split off the base64 data const base64String = uri.split(',')[1] // Read off first 128KB, which is all we need to // get the EXIF data const arr = base64ToUint8Array(base64String, 0, 2 ** 17) try { - exif.load(arr.buffer) - return exif.getTagValue('Orientation') + const tags = ExifReader.load(arr.buffer) + // console.log(tags) + return tags.Orientation } catch (err) { return 1 } diff --git a/client/common/uploadImage.component.js b/client/common/uploadImage.component.js index eb8cc60f..bc88828e 100644 --- a/client/common/uploadImage.component.js +++ b/client/common/uploadImage.component.js @@ -20,7 +20,7 @@ export default class UploadImageComponent extends Component { img.onload = null this.resizeAndUpload(img) } - img.src = fileReaderEvent.result + img.src = fileReaderEvent.target.result } fr.readAsDataURL(files[0]) } @@ -28,6 +28,7 @@ export default class UploadImageComponent extends Component { resizeAndUpload(img) { const canvas = renderThumbnail(img) canvas.toBlob(blob => { + // console.log(blob) this.props.onUpload(blob) }, 'image/jpeg', 80) } diff --git a/client/faceAnalysis/faceAnalysis.actions.js b/client/faceAnalysis/faceAnalysis.actions.js index 6a318b5d..860d3292 100644 --- a/client/faceAnalysis/faceAnalysis.actions.js +++ b/client/faceAnalysis/faceAnalysis.actions.js @@ -8,7 +8,7 @@ import { get, post } from '../util' // urls const url = { - upload: () => process.env.API_HOST + '/task/upload/sleep', + upload: () => process.env.API_HOST + '/task/upload/blur', } export const publicUrl = { } @@ -45,22 +45,6 @@ export const updateOptions = opt => dispatch => { // API functions -export const upload = (payload, file) => dispatch => { - // const { options } = store.getState().faceAnalysis - const tag = 'task' - const fd = new FormData() - fd.append('query_img', file) - // fd.append('limit', options.perPage) - // if (!query) { - dispatch(loading(tag)) - // } - post(url.upload(payload.dataset), fd) - .then(data => { - dispatch(loaded(tag, data)) - }) - .catch(err => dispatch(error(tag, err))) -} - // task polling const POLL_DELAY = 500 @@ -68,12 +52,31 @@ let pollTimeout = null export const poll = (payload, taskURL) => dispatch => { clearTimeout(pollTimeout) + console.log('polling...') get(taskURL) .then(data => { + console.log('poll', data) dispatch(polled(data)) - if (!data.complete) { + if (data.state !== 'error' && data.state !== 'complete') { pollTimeout = setTimeout(() => poll(payload, taskURL), POLL_DELAY) } }) .catch(err => dispatch(error('result', err))) } + +export const upload = (payload, file) => dispatch => { + const tag = 'task' + const fd = new FormData() + fd.append('query_img', file) + dispatch(loading(tag)) + post(url.upload(), fd) + .then(data => { + console.log('loaded!', tag, data) + dispatch(loaded(tag, data)) + const { result, taskURL } = data + if (result && taskURL) { + poll(payload, taskURL)(dispatch) + } + }) + .catch(err => dispatch(error(tag, err))) +} diff --git a/client/faceAnalysis/faceAnalysis.query.js b/client/faceAnalysis/faceAnalysis.query.js index 6b92b70d..a79e3e78 100644 --- a/client/faceAnalysis/faceAnalysis.query.js +++ b/client/faceAnalysis/faceAnalysis.query.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' -import { Loader } from '../common' +import { Loader, UploadImage } from '../common' import * as actions from './faceAnalysis.actions' // function parse_bbox(s) { @@ -37,13 +37,7 @@ class FaceAnalysisQuery extends Component { <div className='uploadContainer'> <div style={style}> {image ? null : <img src="/assets/img/icon_camera.svg" />} - <input - type="file" - name="img" - accept="image/*" - onChange={this.upload.bind(this)} - required - /> + <UploadImage onUpload={this.upload.bind(this)} /> </div> {result.loading && ( <div className='loading' style={style}> diff --git a/client/faceAnalysis/faceAnalysis.reducer.js b/client/faceAnalysis/faceAnalysis.reducer.js index 54a6d5eb..de6e5b0a 100644 --- a/client/faceAnalysis/faceAnalysis.reducer.js +++ b/client/faceAnalysis/faceAnalysis.reducer.js @@ -28,6 +28,7 @@ export default function faceAnalysisReducer(state = initialState(), action) { } case types.faceAnalysis.error: + console.log('error', action) return { ...state, [action.tag]: { error: action.err }, diff --git a/client/faceAnalysis/faceAnalysis.result.js b/client/faceAnalysis/faceAnalysis.result.js index b825a0cb..f9531eba 100644 --- a/client/faceAnalysis/faceAnalysis.result.js +++ b/client/faceAnalysis/faceAnalysis.result.js @@ -26,12 +26,6 @@ const errors = { {"Sorry, an error occured."} </div> ), - bad_dataset: ( - <div> - <h2>{""}</h2> - {""} - </div> - ), not_an_image: ( <div> <h2>{"Not an image"}</h2> @@ -42,19 +36,19 @@ const errors = { class FaceAnalysisResult extends Component { render() { - const { dataset } = this.props.payload - const { query, distances, results, loading, error } = this.props.result + const { query, task, result, loading, error } = this.props.result console.log(this.props.result) if (loading) { return ( <div className='result'> <div> <Loader /><br /> - <h2>Searching...</h2> + <h2>Uploading...</h2> </div> </div> ) } + console.log(task, result) if (error) { // console.log(error) let errorMessage = errors[error] || errors.error @@ -62,53 +56,13 @@ class FaceAnalysisResult extends Component { <div className='result'>{errorMessage}</div> ) } - if (!results) { - return <div className='result'></div> - } - if (!results.length) { - return ( - <div className='result'>{errors.nomatch}</div> - ) - } - const els = results.map((result, i) => { - const distance = distances[i] - const { uuid } = result.file_record - const { x, y, w, h } = result.face_roi - const { fullname, gender, description, images } = result.identity - const bbox = { - left: (100 * x) + '%', - top: (100 * y) + '%', - width: (100 * w) + '%', - height: (100 * h) + '%', - } - // console.log(bbox) - return ( - <div key={i}> - <div className='img'> - <img src={'https://megapixels.nyc3.digitaloceanspaces.com/v1/media/' + dataset + '/' + uuid + '.jpg'} /> - <div className='bbox' style={bbox} /> - </div> - {fullname} {'('}{gender}{')'}<br/> - {description}<br/> - {courtesyS(images, 'image')}{' in dataset'}<br /> - {Math.round((1 - distance) * 100)}{'% match'} - </div> - ) - }) + if (!task && !result) return return ( <div className='result'> <div className="about"> - <h2>Did we find you?</h2> - {'These faces matched images in the '} - <b><tt>{dataset}</tt></b> - {' dataset with over 70% probability.'} - <br /> <small>Query took {query.timing.toFixed(2)} seconds</small> </div> - <div className='results'> - {els} - </div> </div> ) } diff --git a/megapixels/app/server/api_task.py b/megapixels/app/server/api_task.py index 23e11454..fb24c154 100644 --- a/megapixels/app/server/api_task.py +++ b/megapixels/app/server/api_task.py @@ -3,6 +3,7 @@ import re import uuid import time import dlib +import tempfile import simplejson as json import numpy as np from flask import Blueprint, request, jsonify @@ -64,62 +65,51 @@ def sleep_test(): Test the Celery system using a task that sleeps. """ async_task = task_lookup['sleep']['task'].apply_async(args=['sleep_test']) - task_url = '/task/{}/{}'.format('sleep', async_task.id) + task_url = '/task/status/{}/{}'.format('sleep', async_task.id) return jsonify({ 'result': True, 'task_url': task_url, }) -@api_task.route('/upload/:style', methods=['POST']) -def upload(style): +@api_task.route('/upload/blur', methods=['POST']) +def upload(): """ Process a images in a particular style """ + style = 'blur' print('style: {}'.format(style)) if style in task_lookup: task = task_lookup[style]['task'] - print('task',task) + print('task', task) else: return jsonify({ 'result': False, 'error': 'Unknown task', }) - file = request.files['user_image'] - ext = request.form['ext'] - if ext is None: - ext = request.files['ext'] + print('get file...') + file = request.files['query_img'] - uuid_name = str(uuid.uuid4()) + uuid_str = str(uuid.uuid4()) - app.logger.info('[+] style: {}'.format(style)) - app.logger.info('[+] ext: {}'.format(ext)) - app.logger.info('[+] uuid_name: {}'.format(uuid_name)) - - # convert PNG to JPG - print('[+] Resizing image') + print('[+] style: {}'.format(style)) + print('[+] uuid_name: {}'.format(uuid_str)) im = Image.open(file.stream).convert('RGB') - im = ImageOps.fit(im, (256, 256)) - - # # Save image to disk - # print('[+] Save image to {}'.format(fpath)) - # im.save(fpath, 'JPEG', quality=100) - # im_pil_256 = im.resize((256,256)) + im = ImageOps.fit(im, (256, 256,)) - # print('[+] ensure_np...') - # im_np = imx.ensure_np(im_pil_256) + tmpfile = tempfile.NamedTemporaryFile(delete=False) - celery_result = { - im: im, - } + # Save image to disk + print('[+] Save image to temporary file') + im.save(tmpfile, 'JPEG', quality=80) print('[+] Start celery') - async_task = task.apply_async(args=[uuid_name, celery_result]) - task_url = '/task/{}/{}'.format(style, async_task.id) + async_task = task.apply_async(args=[uuid_str, tmpfile.name]) + task_url = '/task/status/{}/{}'.format(style, async_task.id) return jsonify({ 'result': True, 'taskURL': task_url, - 'uuid': uuid_name + 'uuid': uuid_str }) diff --git a/megapixels/app/server/tasks/blur.py b/megapixels/app/server/tasks/blur.py index 3b7e20be..d1f67f54 100644 --- a/megapixels/app/server/tasks/blur.py +++ b/megapixels/app/server/tasks/blur.py @@ -16,18 +16,13 @@ celery_logger = get_task_logger(__name__) import imutils @celery.task(bind=True) -def blur_task(self, uuid_name, extra): +def blur_task(self, uuid_name, fn): """Process image and update during""" celery_logger.debug('process_image_task, uuid: {}'.format(uuid_name)) files = [] - im = Image.open(os.path.join(upload_dir, uuid_name + '.jpg')).convert('RGB') - im = im.resize((256,256)) - files.append({ - 'title': 'Original image', - 'fn': upload_uri + uuid_name + '.jpg' - }) + im = Image.open(fn).convert('RGB') self.update_state( state = 'PROCESSING', @@ -42,13 +37,13 @@ def blur_task(self, uuid_name, extra): im_blur_pil = ensure_pil(im_blur) fn = uuid_name + '_blur.jpg' - fpath = os.path.join(render_dir, fn) - im_blur_pil.save(fpath, 'JPEG', quality=95) + # fpath = os.path.join(render_dir, fn) + # im_blur_pil.save(fpath, 'JPEG', quality=95) - files.append({ - 'title': 'Blurred image', - 'fn': render_uri + uuid_name + '_blur.jpg' - }) + # files.append({ + # 'title': 'Blurred image', + # 'fn': render_uri + uuid_name + '_blur.jpg' + # }) time.sleep(3) @@ -67,9 +62,9 @@ def blur_task(self, uuid_name, extra): 'files': files } - json_path = os.path.join(json_dir, uuid_name + '.json') - with open(json_path, 'w') as json_file: - json.dump(data, json_file) + # json_path = os.path.join(json_dir, uuid_name + '.json') + # with open(json_path, 'w') as json_file: + # json.dump(data, json_file) celery_logger.debug('ok') diff --git a/package-lock.json b/package-lock.json index da0dfcae..60a74ece 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3427,6 +3427,11 @@ "strip-eof": "^1.0.0" } }, + "exif-reader": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/exif-reader/-/exif-reader-1.0.2.tgz", + "integrity": "sha1-AkCLl7YQKOpPReW4k6g2+aoorE8=" + }, "exifreader": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/exifreader/-/exifreader-2.5.0.tgz", @@ -3498,7 +3503,7 @@ "dependencies": { "array-flatten": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true } @@ -3633,7 +3638,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -4405,7 +4410,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -4418,7 +4423,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -4720,7 +4725,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -4749,7 +4754,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -4835,7 +4840,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -4855,7 +4860,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -5833,7 +5838,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -6717,7 +6722,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -10191,7 +10196,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -10211,7 +10216,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { diff --git a/package.json b/package.json index 1848cc7c..fcabb7e1 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "data-uri-to-buffer": "^2.0.0", "date-fns": "^1.29.0", "dotenv": "^6.0.0", + "exif-reader": "^1.0.2", "exifreader": "^2.5.0", "fetch-jsonp": "^1.1.3", "file-saver": "^2.0.0-rc.3", |
