def upload_plant_snaps(request): plant_name = request.POST['name'] uploaded = 0 for name, value in request.POST.items(): if name == 'name': continue if value == u'': continue logger.debug('Saving snap %r for plant %s', value, plant_name) file_uuid, ext = _save_pic(value, request) doc = { 'user': request.user.id, 'timestamp': datetime.datetime.utcnow(), 'filename': file_uuid + ext, 'plant': plant_name, } res = request.db.index(doc, 'snaps', 'snaptype', file_uuid) if not res['ok']: logger.error("Error while saving index") logger.error(res) raise HTTPServerError() uploaded += 1 request.db.refresh() request.session.flash("Uploaded %d snaps" % uploaded) return HTTPFound(location='/plant/%s' % plant_name)
def compute_features(file_path): """Compute a vector of histogram-style features""" logger.debug("Extracting features for %s", file_path) image = color.rgb2gray(imread(file_path)) fd = hog(image, orientations=8, pixels_per_cell=(16, 16), cells_per_block=(1, 1)) return fd
def snapshot(request): """Index page.""" filename = request.matchdict['file'] settings = dict(request.registry.settings) pic_dir = settings['thumbs.document_root'] file_path = os.path.join(pic_dir, filename) try: height, width = get_img_size(file_path) except IOError: logger.error("Failed to open file with path: %s", file_path, exc_info=True) raise HTTPNotFound("Could not load image for snap %s" % filename) # security loop elmts = filename.split(os.sep) for unsecure in ('.', '..'): if unsecure in elmts: return HTTPNotFound() file_uuid = os.path.splitext(filename)[0] if request.method == 'POST': base = _toint(request.POST['bottom_y']), _toint(request.POST['bottom_x']) top = _toint(request.POST['top_y']), _toint(request.POST['top_x']) warped_img_path, new_base, new_top = warp_img(file_path, base, top) # There should be only one snap_idx, snap_type = 'snaps', 'snaptype' snap = request.db.get(snap_idx, snap_type, file_uuid) warped_filename = os.path.basename(warped_img_path) if snap is not None: snap['warped_filename'] = warped_filename snap['warped'] = True snap['base_x'] = base[0] snap['base_y'] = base[1] snap['top_x'] = top[0] snap['top_y'] = top[1] logger.debug("Warping snap %s", file_uuid) request.db.index(snap, snap_idx, snap_type, file_uuid) request.db.refresh() # Precompute the cached features incrementally compute_features_collection([snap], pic_dir, cache=FEATURES_CACHE) else: logger.warning("Could not find snap for %s", file_uuid) return HTTPFound(location='/warped/%s' % warped_filename) data = {'snapshot': filename, 'width': width, 'height': height} return _basic(request, data)
def upload_plant(request): index, type_, root = 'plants', 'planttype', 'plant' if request.user is None: raise Forbidden() if request.method == 'POST': pic = request.POST.get('picture') name = request.POST.get('name', '') if not name: name = str(uuid4()) if pic not in (None, ''): name, ext = _save_pic(pic, request, name) filename = name + ext else: filename = None # TODO: check if plant exist first # Add to Elastic Search doc = { 'user': request.user.id, 'timestamp': datetime.datetime.utcnow(), 'filename': filename, 'gravatar': gravatar_image_url(request.user.email), 'geo_longitude': request.POST.get('longitude'), 'geo_latitude': request.POST.get('latitude'), 'geo_accuracy': request.POST.get('accuracy'), 'name': name, } logger.debug("Indexing plant %s: %r", name, doc) res = request.db.index(doc, index, type_, name) if not res['ok']: logger.error("Error while creating plant %s", name) logger.error(res) raise HTTPServerError() request.db.refresh() request.session.flash('Plant registered') return HTTPFound(location='/%s/%s' % (root, name)) return _basic(request)
def send_report(rcpt, jobid=None, **options): subject = 'Your Marteau report' root = options.get('report.root', DEFAULT_ROOT) sender = options.get('report.sender', DEFAULT_SENDER) smtp_host = options.get('smtp.host', DEFAULT_SMTP_HOST) smtp_port = int(options.get('smtp.port', DEFAULT_SMTP_PORT)) smtp_user = options.get('smtp.user') smtp_password = options.get('smtp.password') # preparing the message if jobid is None: url = root else: url = '%s/test/%s' % (root, jobid) body = TMPL % url msg = MIMEText(body.encode('utf-8'), 'plain', 'utf8') def _normalize_realname(field): address = AddressList(field).addresslist if len(address) == 1: realname, email = address[0] if realname != '': return '%s <%s>' % (str(Header(realname, 'utf-8')), str(email)) return field msg['From'] = _normalize_realname(sender) msg['To'] = _normalize_realname(rcpt) msg['Subject'] = Header(subject, 'utf-8') logger.debug('Connecting to %s:%d' % (smtp_host, smtp_port)) try: server = smtplib.SMTP(smtp_host, smtp_port, timeout=5) except (smtplib.SMTPConnectError, socket.error), e: return False, str(e)
return '%s <%s>' % (str(Header(realname, 'utf-8')), str(email)) return field msg['From'] = _normalize_realname(sender) msg['To'] = _normalize_realname(rcpt) msg['Subject'] = Header(subject, 'utf-8') logger.debug('Connecting to %s:%d' % (smtp_host, smtp_port)) try: server = smtplib.SMTP(smtp_host, smtp_port, timeout=5) except (smtplib.SMTPConnectError, socket.error), e: return False, str(e) # auth if smtp_user is not None and smtp_password is not None: logger.debug('Login with %r' % smtp_user) try: server.login(smtp_user, smtp_password) except (smtplib.SMTPHeloError, smtplib.SMTPAuthenticationError, smtplib.SMTPException), e: return False, str(e) # the actual sending logger.debug('Sending the mail') try: server.sendmail(sender, [rcpt], msg.as_string()) finally: server.quit() return True, None
def warped(request): """Index page.""" filename = request.matchdict['file'] settings = dict(request.registry.settings) pic_dir = settings['thumbs.document_root'] orig_img = os.path.join(pic_dir, filename) res = get_img_size(orig_img) warped_stemname = os.path.splitext(filename)[0] snap_idx, snap_type = 'snaps', 'snaptype' if warped_stemname.endswith('_warped'): file_uuid = warped_stemname[:-len('_warped')] else: logger.warning( 'Warped stemname is expected to end with "_warped", got: %s', warped_stemname ) file_uuid = warped_stemname try: current_snap = request.db.get(snap_idx, snap_type, file_uuid) except NotFoundException: raise HTTPNotFound("Could not find snapshot for %s" % file_uuid) next_snap = None unwarped_count = "" if not current_snap.plant: query = FieldQuery(FieldParameter('warped', True)) candidates = request.db.search(query, size=200, indices=['snaps'], sort='timestamp:desc') # TODO: filter out non-plant snaps in the query directly candidates = [c for c in candidates if c.plant != None] logger.debug('Computing suggestion for %s', current_snap.filename) best_snaps, scores = suggest_snaps(current_snap, candidates, pic_dir, FEATURES_CACHE) suggestions = OrderedDict() for i, (snap, score) in enumerate(zip(best_snaps, scores)): name = snap.plant logger.debug("#%02d: %s with score = %f - %s", i, name, score, snap.filename) suggestion = suggestions.get(name) if suggestion is None: query = FieldQuery(FieldParameter('name', name)) plants = list(request.db.search(query, indices=['plants'])) if not plants: raise HTTPNotFound("No plant registered under %s" % name) plant = plants[0] suggestions[name] = (plant, [snap]) else: suggestion[1].append(snap) else: suggestions = None # Suggest to warp the next unwarped snapshot from the same plant query = FieldQuery(fieldparameters=( FieldParameter('warped', None), FieldParameter('filename', '-' + current_snap.filename), FieldParameter('plant', current_snap.plant))) max_count = 100 next_snaps = request.db.search({ "bool" : { "must" : [ {'field': {'filename': '-' + current_snap.filename}}, {'field': {'plant': current_snap.plant}}, ], "must_not" : [ {'field': {'warped': True}}, ], }, }, size=max_count, indices=['snaps'], sort='timestamp:desc') logger.debug(len(next_snaps)) if len(next_snaps) > 0: next_snap = next_snaps[0] unwarped_count = "%d" % len(next_snaps) if len(next_snaps) == max_count: unwarped_count += "+" if len(res) == 2: height, width = res else: height, width = 500, 500 # security loop elmts = filename.split(os.sep) for unsecure in ('.', '..'): if unsecure in elmts: return HTTPNotFound() data = {'snapshot': filename, 'original': get_original_path(filename), 'width': width, 'height': height, 'snap': current_snap, 'uuid': file_uuid, 'suggestions': suggestions, 'next_snap': next_snap, 'unwarped_count': unwarped_count} return _basic(request, data)
def warp_img(raw_img_path, base, top, warped_img_size=WARPED_IMG_SIZE): """Rotate and rescale to normalize the position of base and top points Return the file system path of the transformed image and the transformed base and top positions in the new image. """ # Load the data original = imread(raw_img_path) base = np.array(base) top = np.array(top) # Check the expected size of the resulting file warped_img_size = np.array(warped_img_size) warped_h, warped_w = warped_img_size bigger = np.max(original.shape[:2]) * 1.5 embedded = np.zeros(shape=(bigger, bigger, original.shape[2]), dtype=original.dtype) original_center = (base + top) / 2 embedded_center = np.array(embedded.shape[:2]) / 2 offset = embedded_center - original_center x_slice = slice(offset[0], offset[0] + original.shape[0]) y_slice = slice(offset[1], offset[1] + original.shape[1]) try: embedded[x_slice, y_slice, :] = original except ValueError: logger.warning("Invalid base and top positions on image %s: %r, %r", raw_img_path, base, top) return raw_img_path, tuple(base), tuple(top) # Check the original base and top coordinate positions embedded_base = base + offset embedded_top = top + offset warped_base = ((0.9, 0.5) * warped_img_size).astype(np.int) warped_top = ((0.05, 0.5) * warped_img_size).astype(np.int) logger.debug("About to warp from {base, top = %r, %r} to " "{warped_base, warped_top = %r, %r}", base, top, warped_base, warped_top) orig_vector = embedded_top - embedded_base orig_norm = np.linalg.norm(orig_vector) warped_vector = warped_top - warped_base warped_norm = np.linalg.norm(warped_vector) if orig_norm == 0: logger.warning("Cannot warp image using a null original vector") # Cannot warp with this data, return null operation insted return raw_img_path, tuple(base), tuple(top) scale = warped_norm / orig_norm rotation = (np.arctan2(warped_vector[0], warped_vector[1]) - np.arctan2(orig_vector[0], orig_vector[1])) rotation_degrees = rotation * -180. / np.pi pil_rotated = Image.fromarray(embedded).rotate(rotation_degrees) scaled_size = (int(embedded.shape[0] * scale), int(embedded.shape[1] * scale)) logger.debug("Warping from %r to %r using rot=%0.2f scale=%0.2f", orig_vector, warped_vector, rotation_degrees, scale) pil_scaled = pil_rotated.resize(scaled_size, Image.ANTIALIAS) scaled = np.array(pil_scaled) scaled_center = np.array(scaled.shape[:2]) / 2 x_slice = slice(scaled_center[0] - warped_h / 2, scaled_center[0] + warped_h / 2) y_slice = slice(scaled_center[1] - warped_w / 2, scaled_center[1] + warped_w / 2) warped = scaled[x_slice, y_slice, :] # Save the result back on the harddrive warped_path = get_warped_img_path(raw_img_path) imsave(warped_path, warped) return warped_path, tuple(warped_base), tuple(warped_top)