#!/usr/bin/python # -*- coding: UTF-8 -*- from flask import Blueprint from flask import current_app, abort, send_file, jsonify import base64 from cStringIO import StringIO from threading import Lock from uuid import uuid4 from PIL import Image import utils from utils.decorator import login_required from utils.images import create_thumbnail from functions import do_decrypt_dek, do_encrypt_dek from functions import no_preview_image from views.tp_template import get_tenprint_template_zones from NIST import NISTf import config lock = Lock() image_view = Blueprint( "image", __name__, template_folder = "template" ) def get_submission_uuid_for_file( file_uuid ): """ Get the related submission uuid for a file uuid. """ sql = """ SELECT submissions.uuid FROM submissions LEFT JOIN files ON submissions.id = files.folder WHERE files.uuid = %s """ return config.db.query_fetchone( sql, ( file_uuid, ) )[ "uuid" ] @image_view.route( config.baseurl + "/image/file//preview" ) @login_required def image_file_serve( file_id ): """ Function to get an image from the database and return it as PNG preview image. """ current_app.logger.info( "Serve a preview for the file '{}'".format( file_id ) ) try: submission_id = get_submission_uuid_for_file( file_id ) current_app.logger.debug( "submission id: '{}'".format( submission_id ) ) img, _ = image_serve( "thumbnails", file_id, submission_id ) if img == None: current_app.logger.debug( "No image in the 'thumnnails' database. Recreating the thumbnail" ) img, _ = image_serve( "files", file_id, submission_id ) if img == None: return abort( 404 ) current_app.logger.debug( "Image from the 'files' table: {}".format( img ) ) img = create_thumbnail( file_id, img, submission_id ) current_app.logger.debug( "Thumbnail image: {}".format( img ) ) buff = utils.images.pil2buffer( img, "PNG" ) return send_file( buff, mimetype = "image/png" ) except: current_app.logger.error( "Error while creating the thumbnail. Serving a 'no preview' image" ) return send_file( no_preview_image(), mimetype = "image/png" ) @image_view.route( config.baseurl + "/image/segment//" ) @login_required def image_segment_serve( tenprint_id, pc ): """ Serve a preview for a segment image. """ current_app.logger.info( "Serving a segment image for the tenprint '{}', pc '{}'".format( tenprint_id, pc ) ) try: submission_id = get_submission_uuid_for_file( tenprint_id ) current_app.logger.debug( "submission id: {}".format( submission_id ) ) img, file_segment_id = image_serve( "files_segments", ( tenprint_id, pc ), submission_id ) img = create_thumbnail( file_segment_id, img, submission_id ) current_app.logger.debug( "image: {}".format( img ) ) buff = utils.images.pil2buffer( img, "PNG" ) return send_file( buff, mimetype = "image/png" ) except: current_app.logger.error( "Error while creating the thumbnail. Serving a 'no preview' image" ) return send_file( no_preview_image(), mimetype = "image/png" ) @image_view.route( config.baseurl + "/image/template//" ) @image_view.route( config.baseurl + "/image/template///" ) @login_required def image_tp_template( tenprint_id, side, action = "full" ): """ Serve a template image, full-resolution or preview. """ current_app.logger.info( "Serve the tenprint template image for {}, {}".format( tenprint_id, side ) ) if side in [ "front", "back" ]: img, _ = image_serve( "tenprint_cards", ( tenprint_id, side ), None ) if img == None: return send_file( no_preview_image(), mimetype = "image/png" ) if action == "preview": img.thumbnail( ( 500, 500 ) ) current_app.logger.debug( "image: {}".format( img ) ) buff = utils.images.pil2buffer( img, "PNG" ) return send_file( buff, mimetype = "image/png" ) else: return abort( 403 ) def image_serve( table, image_id, submission_id ): """ Backend function to get the image from the database. """ current_app.logger.info( "Serve the image '{}' for submission '{}' from table '{}'".format( image_id, submission_id, table ) ) need_to_decrypt = True if table == "files_segments": if isinstance( image_id, tuple ): tp, pc = image_id sql = "SELECT data, uuid FROM {} WHERE tenprint = %s AND pc = %s".format( table ) p = ( tp, pc, ) else: sql = "SELECT data, uuid FROM {} WHERE uuid = %s".format( table ) p = ( image_id, ) elif table in [ "files", "thumbnails" ]: sql = "SELECT data, uuid, format FROM {} WHERE uuid = %s".format( table ) p = ( image_id, ) elif table == "tenprint_cards": image_id, t = image_id sql = "SELECT image_{} as data, id as uuid FROM {} WHERE id = %s".format( t, table ) p = ( image_id, ) need_to_decrypt = False else: current_app.logger.error( "table '{}' not authorized".format( table ) ) raise Exception( "table not authorized" ) current_app.logger.debug( "sql: {}".format( sql ) ) current_app.logger.debug( "params: {}".format( p ) ) data = config.db.query_fetchone( sql, p ) if data == None: return None, None else: img = data[ "data" ] rid = data[ "uuid" ] if img == None: return None, None current_app.logger.debug( "image: {}...".format( img[ 0:20 ] ) ) current_app.logger.debug( "need_to_decrypt: {}".format( need_to_decrypt ) ) if "format" in data: current_app.logger.debug( "format: {}".format( data[ "format" ] ) ) if need_to_decrypt: img = do_decrypt_dek( img, submission_id ) if table == "files" and data[ "format" ].upper() == "NIST": img = str2nist2img( img ) else: img = str2img( img ) current_app.logger.debug( "image: {}".format( img ) ) return img, rid def str2img( data ): """ Convert a base64 string image to a PIL image. """ current_app.logger.info( "Convert string image to PIL format" ) if data == None: return None else: img = base64.b64decode( data ) buff = StringIO() buff.write( img ) buff.seek( 0 ) img = Image.open( buff ) current_app.logger.debug( "string: {}".format( data[ 0:20 ] ) ) current_app.logger.debug( "image: {}".format( img ) ) return img def str2nist2img( data ): current_app.logger.info( "Convert string NIST file to tenprint card image" ) if data == None: return None else: img = base64.b64decode( data ) buff = StringIO() buff.write( img ) buff.seek( 0 ) with lock: n = NISTf( buff ) img = n.get_tenprintcard_front( 1000 ) current_app.logger.debug( "string: {}".format( data[ 0:20 ] ) ) current_app.logger.debug( "image: {}".format( img ) ) return img @image_view.route( config.baseurl + "/image/file//info" ) @login_required def img_info( image_id ): """ Get and return the metadata for a particular image. See do_img_info() for more informations. """ current_app.logger.info( "Serve image informations for image '{}'".format( image_id ) ) d = do_img_info( image_id ) if d != None: return jsonify( d ) else: return abort( 404 ) def do_img_info( image_id ): """ Retrieve the metadata for a particular image from the database. """ current_app.logger.debug( "Get image information from database for '{}'".format( image_id ) ) sql = "SELECT size, width, height, resolution, format FROM files WHERE uuid = %s" d = config.db.query_fetchone( sql, ( image_id, ) ) for key, value in d.iteritems(): current_app.logger.debug( "{}: {}".format( key, value ) ) if d != None: return dict( d ) else: return None @image_view.route( config.baseurl + "/image/segment//start" ) @login_required def image_tenprint_segmentation( tenprint_id ): """ Route to start the segmentation of a tenprint image into segments (fingers or palm images). """ current_app.logger.info( "Start segmentations for '{}'".format( tenprint_id ) ) ret = do_image_tenprint_segmentation( tenprint_id ) return jsonify( { "error": False, "data": ret } ) def do_image_tenprint_segmentation( tenprint_id ): """ Backend function to create all the segments images for a tenprint souce image. """ sql = "SELECT size, resolution, type, format, data FROM files WHERE uuid = %s" img = config.db.query_fetchone( sql, ( tenprint_id, ) ) for key, value in img.iteritems(): if isinstance( value, str ) and len( value ) > 20: value = "{}...".format( value[ 0:20 ] ) current_app.logger.debug( "{}: {}".format( key, value ) ) res = img[ "resolution" ] img_format = img[ "format" ] side = { 1: "front", 2: "back" }[ img[ "type" ] ] current_app.logger.debug( "side: {}".format( side ) ) submission_id = get_submission_uuid_for_file( tenprint_id ) current_app.logger.debug( "Decrypt data with DEK" ) img = do_decrypt_dek( img[ "data" ], submission_id ) img = base64.b64decode( img ) buff = StringIO() buff.write( img ) buff.seek( 0 ) img = Image.open( buff ) current_app.logger.debug( "image: {}".format( img ) ) sql = "SELECT * FROM segments_locations WHERE tenprint_id = %s" data = ( tenprint_id, ) zones = config.db.query_fetchall( sql, data ) for z in zones: current_app.logger.debug( "Segmenting fpc '{}'".format( z[ "fpc" ] ) ) tl_x, tl_y, br_x, br_y = [ z[ "x" ], z[ "y" ], z[ "x" ] + z[ "width" ], z[ "y" ] + z[ "height" ] ] tmp = img.crop( ( tl_x, tl_y, br_x, br_y ) ) buff = StringIO() tmp.save( buff, format = img_format ) buff.seek( 0 ) current_app.logger.debug( "Encrypting segment image with DEK" ) file_data = buff.getvalue() file_data = base64.b64encode( file_data ) file_data = do_encrypt_dek( file_data, submission_id ) sql = "SELECT id FROM files_segments WHERE tenprint = %s AND pc = %s" q = config.db.query_fetchone( sql, ( tenprint_id, z[ "fpc" ], ) ) if q == None: current_app.logger.debug( "Inserting to the database" ) sql = utils.sql.sql_insert_generate( "files_segments", [ "tenprint", "uuid", "pc", "data" ] ) data = ( tenprint_id, str( uuid4() ), z[ "fpc" ], file_data ) config.db.query( sql, data ) else: current_app.logger.debug( "Updating the database" ) sql = "UPDATE files_segments SET data = %s WHERE tenprint = %s AND pc = %s" data = ( file_data, tenprint_id, z[ "fpc" ] ) config.db.query( sql, data ) config.db.commit() return True