Download app.py from 53dNorthA/CarImageClassification2: direct link, hf CLI and curl.
- Browser
- Download file 6.02 kB
-
https://huggingface.co/spaces/53dNorthA/CarImageClassification2/resolve/main/app.py
- Command line
-
hf download hf://spaces/53dNorthA/CarImageClassification2/app.py
-
curl -L -o app.py https://huggingface.co/spaces/53dNorthA/CarImageClassification2/resolve/main/app.py
6.02 kB
| import os | |
| import traceback | |
| import uuid | |
| import torch | |
| from dotenv import load_dotenv | |
| from flask import Flask, jsonify, render_template, request | |
| from flask_cors import CORS | |
| from ultralytics import YOLO | |
| from werkzeug.utils import secure_filename | |
| from processing import process_images | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| load_dotenv(os.path.join(BASE_DIR, '.env')) | |
| app = Flask(__name__) | |
| CORS(app) | |
| ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'} | |
| def resolve_app_path(configured_path, default_relative_path): | |
| """Resolve configured relative paths from the application directory.""" | |
| path = configured_path or default_relative_path | |
| if os.path.isabs(path): | |
| return path | |
| return os.path.join(BASE_DIR, path) | |
| UPLOAD_FOLDER = resolve_app_path(os.getenv('UPLOAD_FOLDER'), os.path.join('static', 'uploads')) | |
| MODELS_FOLDER = resolve_app_path(os.getenv('MODELS_FOLDER'), 'models') | |
| PARTS_MODEL_NAME = os.getenv('PARTS_MODEL_NAME', 'best_parts_EP336.pt') | |
| DAMAGE_MODEL_NAME = os.getenv('DAMAGE_MODEL_NAME', 'best_new_EP382.pt') | |
| def resolve_model_path(model_name): | |
| """Resolve an absolute model path or a filename relative to MODELS_FOLDER.""" | |
| if os.path.isabs(model_name): | |
| return model_name | |
| return os.path.join(MODELS_FOLDER, model_name) | |
| PARTS_MODEL_PATH = resolve_model_path(PARTS_MODEL_NAME) | |
| DAMAGE_MODEL_PATH = resolve_model_path(DAMAGE_MODEL_NAME) | |
| app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER | |
| os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) | |
| os.makedirs(MODELS_FOLDER, exist_ok=True) | |
| device = 'cuda' if torch.cuda.is_available() else 'cpu' | |
| print(f'Using device: {device}') | |
| def load_model(model_path, label): | |
| """Load a YOLO model if its configured file exists.""" | |
| if not os.path.isfile(model_path): | |
| print(f'Warning: {label} model file not found at {model_path}') | |
| return None | |
| try: | |
| model = YOLO(model_path) | |
| model.to(device) | |
| print(f"Successfully loaded {label.lower()} model '{os.path.basename(model_path)}' on {device}.") | |
| return model | |
| except Exception as exc: | |
| print(f'Error loading {label} model ({model_path}): {exc}') | |
| traceback.print_exc() | |
| return None | |
| parts_model = load_model(PARTS_MODEL_PATH, 'Parts') | |
| damage_model = load_model(DAMAGE_MODEL_PATH, 'Damage') | |
| def allowed_file(filename): | |
| """Return True when filename has an allowed image extension.""" | |
| return bool(filename) and '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS | |
| def model_readiness_error(): | |
| """Return a useful model readiness payload, or None when both models are loaded.""" | |
| missing = [] | |
| if parts_model is None: | |
| missing.append({'model': 'parts', 'path': PARTS_MODEL_PATH}) | |
| if damage_model is None: | |
| missing.append({'model': 'damage', 'path': DAMAGE_MODEL_PATH}) | |
| if not missing: | |
| return None | |
| return { | |
| 'error': 'Prediction models are not ready.', | |
| 'missing_or_unloaded_models': missing, | |
| 'hint': 'Place the configured .pt files at the listed paths or update MODELS_FOLDER/model names in .env.' | |
| } | |
| def home(): | |
| """Serve the main HTML page.""" | |
| return render_template('index.html') | |
| def health(): | |
| """Expose basic service/model readiness without running inference.""" | |
| readiness = model_readiness_error() | |
| if readiness: | |
| return jsonify({'status': 'degraded', **readiness}), 503 | |
| return jsonify({'status': 'ok', 'device': device}), 200 | |
| def predict(): | |
| """Receive one or more images and return part/damage predictions.""" | |
| readiness = model_readiness_error() | |
| if readiness: | |
| return jsonify(readiness), 503 | |
| raw_session_key = request.form.get('session_key', '').strip() | |
| session_key = secure_filename(raw_session_key)[:100] or uuid.uuid4().hex | |
| if 'file' not in request.files: | |
| return jsonify({'error': 'No file part in the request'}), 400 | |
| files = request.files.getlist('file') | |
| if not files or all(not file.filename for file in files): | |
| return jsonify({'error': 'No selected files'}), 400 | |
| saved_images = [] | |
| skipped_files = [] | |
| try: | |
| for index, file in enumerate(files): | |
| original_filename = file.filename or '' | |
| if not (file and allowed_file(original_filename)): | |
| skipped_files.append(original_filename or f'file_{index}') | |
| continue | |
| safe_original = secure_filename(original_filename) or f'image_{index}.jpg' | |
| unique_filename = f'{uuid.uuid4().hex}_{safe_original}' | |
| filepath = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename) | |
| file.save(filepath) | |
| saved_images.append({ | |
| 'path': filepath, | |
| 'filename': original_filename, | |
| 'index': index, | |
| }) | |
| if not saved_images: | |
| return jsonify({ | |
| 'error': 'No valid files were uploaded. Allowed types: png, jpg, jpeg', | |
| 'skipped_files': skipped_files, | |
| }), 400 | |
| print(f"Processing {len(saved_images)} file(s) for session '{session_key}'...") | |
| results = process_images(parts_model, damage_model, saved_images) | |
| print(f"Processing complete for session '{session_key}'.") | |
| return jsonify(results) | |
| except Exception as exc: | |
| print(f'An error occurred during processing for session {session_key}: {exc}') | |
| traceback.print_exc() | |
| return jsonify({'error': f'An error occurred during processing: {exc}'}), 500 | |
| finally: | |
| for image in saved_images: | |
| filepath = image['path'] | |
| try: | |
| if os.path.exists(filepath): | |
| os.remove(filepath) | |
| except Exception as exc: | |
| print(f'Error cleaning up file {filepath}: {exc}') | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860, debug=False) | |