import os import subprocess import logging from flask import Flask, request, send_file, jsonify from werkzeug.utils import secure_filename from flask_cors import CORS # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Define directories for uploads and outputs UPLOAD_FOLDER = 'uploads_gradio' OUTPUT_FOLDER = 'outputs_gradio' # Create directories if they don't exist os.makedirs(UPLOAD_FOLDER, exist_ok=True) os.makedirs(OUTPUT_FOLDER, exist_ok=True) # Initialize Flask app app = Flask(__name__) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # Enable CORS for all routes (optional, adjust origins as needed) CORS(app) # Allowed file extensions ALLOWED_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.bmp'} def allowed_file(filename): """Check if the file has an allowed extension.""" _, ext = os.path.splitext(filename) return ext.lower() in ALLOWED_EXTENSIONS def animate_image(file_path): """ Process the uploaded image and generate an animated GIF. Args: file_path (str): Path to the uploaded file. Returns: str: Path to the generated GIF. """ if not file_path: raise ValueError("No file uploaded.") input_path = file_path filename = os.path.basename(input_path) base, ext = os.path.splitext(filename) # Define the annotation directory for this specific image char_anno_dir = os.path.join(OUTPUT_FOLDER, f"{base}_out") os.makedirs(char_anno_dir, exist_ok=True) # Validate file extension if ext.lower() not in ALLOWED_EXTENSIONS: raise ValueError("Unsupported file type. Please upload an image file (png, jpg, jpeg, bmp).") try: logger.info(f"Starting animation for {input_path}") # Run the image_to_animation.py script with required arguments subprocess.run([ 'python', 'examples/image_to_animation.py', input_path, char_anno_dir ], check=True) # Path to the generated GIF gif_path = os.path.join(char_anno_dir, "video.gif") if os.path.exists(gif_path): logger.info(f"Animation successful: {gif_path}") return gif_path else: raise FileNotFoundError("Animation failed to generate. Please ensure the input image contains clear humanoid drawings.") except subprocess.CalledProcessError as e: logger.error(f"Error during processing: {e}") raise RuntimeError(f"Error during processing: {e}") except Exception as e: logger.error(f"Unexpected error: {e}") raise RuntimeError(f"Unexpected error: {e}") @app.route('/animate', methods=['POST']) def animate(): """ Endpoint to receive an image and return the animated GIF. """ logger.info("Received request to /animate") if 'file' not in request.files: logger.warning("No file part in the request.") return jsonify({"error": "No file part in the request."}), 400 file = request.files['file'] if file.filename == '': logger.warning("No file selected for uploading.") return jsonify({"error": "No file selected for uploading."}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) input_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(input_path) logger.info(f"File saved to {input_path}") try: gif_path = animate_image(input_path) logger.info(f"Sending GIF: {gif_path}") return send_file( gif_path, mimetype='image/gif', as_attachment=True, download_name=f"{os.path.splitext(filename)[0]}.gif" ) except Exception as e: logger.error(f"Error in /animate: {e}") return jsonify({"error": str(e)}), 500 else: logger.warning("Allowed file types are png, jpg, jpeg, bmp.") return jsonify({"error": "Allowed file types are png, jpg, jpeg, bmp."}), 400 @app.route('/', methods=['GET']) def index(): """ Root endpoint to provide basic information. """ logger.info("Received request to /") return jsonify({ "message": "Animated Drawings API", "endpoints": { "/animate": "POST an image to receive an animated GIF." } }) @app.route('/health', methods=['GET']) def health(): """ Health check endpoint. """ logger.info("Received request to /health") return jsonify({"status": "healthy"}), 200 if __name__ == '__main__': # Use the PORT environment variable provided by Hugging Face Spaces or default to 7860 port = int(os.getenv("PORT", "7860")) logger.info(f"Starting Flask app on port {port}") app.run(host='0.0.0.0', port=port)