Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, File, UploadFile | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import HTMLResponse | |
| import numpy as np | |
| from PIL import Image | |
| from tensorflow.keras.models import load_model | |
| import os | |
| os.environ["KERAS_BACKEND"] = "tensorflow" | |
| app = FastAPI() | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| ANIMALS = ['Cat', 'Dog', 'Panda'] # Animal names here, these represent the labels of the images that we trained our model on. | |
| def test_upload(): | |
| html_content = """ | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Animal Image Classifier</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| max-width: 800px; | |
| margin: 50px auto; | |
| padding: 20px; | |
| background-color: #f5f5f5; | |
| } | |
| .container { | |
| background-color: white; | |
| padding: 30px; | |
| border-radius: 10px; | |
| box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
| } | |
| h1 { | |
| color: #333; | |
| text-align: center; | |
| } | |
| .upload-section { | |
| margin: 20px 0; | |
| padding: 20px; | |
| border: 2px dashed #ccc; | |
| border-radius: 5px; | |
| text-align: center; | |
| } | |
| input[type="file"] { | |
| margin: 10px 0; | |
| } | |
| button { | |
| background-color: #4CAF50; | |
| color: white; | |
| padding: 10px 20px; | |
| border: none; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| } | |
| button:hover { | |
| background-color: #45a049; | |
| } | |
| button:disabled { | |
| background-color: #cccccc; | |
| cursor: not-allowed; | |
| } | |
| .preview { | |
| margin: 20px 0; | |
| text-align: center; | |
| } | |
| .preview img { | |
| max-width: 300px; | |
| max-height: 300px; | |
| border-radius: 5px; | |
| box-shadow: 0 2px 5px rgba(0,0,0,0.2); | |
| } | |
| .result { | |
| margin-top: 20px; | |
| padding: 20px; | |
| background-color: #e8f5e9; | |
| border-radius: 5px; | |
| text-align: center; | |
| font-size: 20px; | |
| font-weight: bold; | |
| color: #2e7d32; | |
| } | |
| .error { | |
| background-color: #ffebee; | |
| color: #c62828; | |
| } | |
| .loading { | |
| display: none; | |
| margin: 20px 0; | |
| text-align: center; | |
| } | |
| .spinner { | |
| border: 4px solid #f3f3f3; | |
| border-top: 4px solid #4CAF50; | |
| border-radius: 50%; | |
| width: 40px; | |
| height: 40px; | |
| animation: spin 1s linear infinite; | |
| margin: 0 auto; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>๐พ Animal Image Classifier</h1> | |
| <p style="text-align: center; color: #666;">Upload an image of a Cat, Dog, or Panda to classify it!</p> | |
| <div class="upload-section"> | |
| <input type="file" id="imageInput" accept="image/*"> | |
| <br> | |
| <button onclick="uploadImage()" id="uploadBtn">Classify Image</button> | |
| </div> | |
| <div class="loading" id="loading"> | |
| <div class="spinner"></div> | |
| <p>Classifying...</p> | |
| </div> | |
| <div class="preview" id="preview"></div> | |
| <div id="result"></div> | |
| </div> | |
| <script> | |
| let selectedFile = null; | |
| document.getElementById('imageInput').addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| selectedFile = file; | |
| // Show preview | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| document.getElementById('preview').innerHTML = | |
| '<img src="' + e.target.result + '" alt="Preview">'; | |
| } | |
| reader.readAsDataURL(file); | |
| document.getElementById('result').innerHTML = ''; | |
| } | |
| }); | |
| async function uploadImage() { | |
| if (!selectedFile) { | |
| alert('Please select an image first!'); | |
| return; | |
| } | |
| const uploadBtn = document.getElementById('uploadBtn'); | |
| const loading = document.getElementById('loading'); | |
| const resultDiv = document.getElementById('result'); | |
| // Show loading, disable button | |
| uploadBtn.disabled = true; | |
| loading.style.display = 'block'; | |
| resultDiv.innerHTML = ''; | |
| const formData = new FormData(); | |
| formData.append('img', selectedFile); | |
| try { | |
| const response = await fetch('/upload/image', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| if (response.ok) { | |
| const result = await response.text(); | |
| const animal = result.replace(/"/g, ''); // Remove quotes if present | |
| // Display result with emoji | |
| const emojis = { | |
| 'Cat': '๐ฑ', | |
| 'Dog': '๐ถ', | |
| 'Panda': '๐ผ' | |
| }; | |
| resultDiv.innerHTML = | |
| '<div class="result">Prediction: ' + | |
| (emojis[animal] || '') + ' ' + animal + '</div>'; | |
| } else { | |
| resultDiv.innerHTML = | |
| '<div class="result error">Error: ' + response.status + '</div>'; | |
| } | |
| } catch (error) { | |
| resultDiv.innerHTML = | |
| '<div class="result error">Error: ' + error.message + '</div>'; | |
| } finally { | |
| // Hide loading, enable button | |
| loading.style.display = 'none'; | |
| uploadBtn.disabled = false; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| return HTMLResponse(content=html_content) | |
| model = load_model("hf://nathansegers/masterclass-2025") | |
| async def uploadImage(img: UploadFile = File(...)): | |
| original_image = Image.open(img.file) # Read the bytes and process as an image | |
| resized_image = original_image.resize((64, 64)) # Resize | |
| images_to_predict = np.expand_dims(np.array(resized_image), axis=0) # Our AI Model wanted a list of images, but we only have one, so we expand it's dimension | |
| predictions = model.predict(images_to_predict) # The result will be a list with predictions in the one-hot encoded format: [ [0 1 0] ] | |
| prediction_probabilities = predictions | |
| classifications = prediction_probabilities.argmax(axis=1) # We try to fetch the index of the highest value in this list [ [1] ] | |
| return ANIMALS[classifications.tolist()[0]] # Fetch the first item in our classifications array, format it as a list first, result will be e.g.: "Dog" | |