Spaces:
Sleeping
Sleeping
Amina commited on
Commit ·
d53b194
1
Parent(s): 6f75e04
Add files with LFS tracking
Browse files- .idea/.gitignore +8 -0
- .idea/car-damage-app.iml +10 -0
- .idea/inspectionProfiles/Project_Default.xml +12 -0
- .idea/inspectionProfiles/profiles_settings.xml +6 -0
- .idea/misc.xml +6 -0
- .idea/modules.xml +8 -0
- .idea/vcs.xml +6 -0
- Dockerfile +13 -0
- app.py +39 -0
- best.pt +3 -0
- index.html +118 -0
- requirements.txt +6 -0
.idea/.gitignore
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Default ignored files
|
| 2 |
+
/shelf/
|
| 3 |
+
/workspace.xml
|
| 4 |
+
# Editor-based HTTP Client requests
|
| 5 |
+
/httpRequests/
|
| 6 |
+
# Datasource local storage ignored files
|
| 7 |
+
/dataSources/
|
| 8 |
+
/dataSources.local.xml
|
.idea/car-damage-app.iml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<module type="PYTHON_MODULE" version="4">
|
| 3 |
+
<component name="NewModuleRootManager">
|
| 4 |
+
<content url="file://$MODULE_DIR$">
|
| 5 |
+
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
| 6 |
+
</content>
|
| 7 |
+
<orderEntry type="jdk" jdkName="Python 3.12 (car-damage-app)" jdkType="Python SDK" />
|
| 8 |
+
<orderEntry type="sourceFolder" forTests="false" />
|
| 9 |
+
</component>
|
| 10 |
+
</module>
|
.idea/inspectionProfiles/Project_Default.xml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<component name="InspectionProjectProfileManager">
|
| 2 |
+
<profile version="1.0">
|
| 3 |
+
<option name="myName" value="Project Default" />
|
| 4 |
+
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
| 5 |
+
<option name="ignoredErrors">
|
| 6 |
+
<list>
|
| 7 |
+
<option value="N806" />
|
| 8 |
+
</list>
|
| 9 |
+
</option>
|
| 10 |
+
</inspection_tool>
|
| 11 |
+
</profile>
|
| 12 |
+
</component>
|
.idea/inspectionProfiles/profiles_settings.xml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<component name="InspectionProjectProfileManager">
|
| 2 |
+
<settings>
|
| 3 |
+
<option name="USE_PROJECT_PROFILE" value="false" />
|
| 4 |
+
<version value="1.0" />
|
| 5 |
+
</settings>
|
| 6 |
+
</component>
|
.idea/misc.xml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<project version="4">
|
| 3 |
+
<component name="Black">
|
| 4 |
+
<option name="sdkName" value="Python 3.12 (car-damage-app)" />
|
| 5 |
+
</component>
|
| 6 |
+
</project>
|
.idea/modules.xml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<project version="4">
|
| 3 |
+
<component name="ProjectModuleManager">
|
| 4 |
+
<modules>
|
| 5 |
+
<module fileurl="file://$PROJECT_DIR$/.idea/car-damage-app.iml" filepath="$PROJECT_DIR$/.idea/car-damage-app.iml" />
|
| 6 |
+
</modules>
|
| 7 |
+
</component>
|
| 8 |
+
</project>
|
.idea/vcs.xml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<project version="4">
|
| 3 |
+
<component name="VcsDirectoryMappings">
|
| 4 |
+
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
| 5 |
+
</component>
|
| 6 |
+
</project>
|
Dockerfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.9-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /code
|
| 4 |
+
|
| 5 |
+
COPY ./requirements.txt /code/requirements.txt
|
| 6 |
+
|
| 7 |
+
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
| 8 |
+
|
| 9 |
+
COPY ./best.pt /code/best.pt
|
| 10 |
+
COPY ./app.py /code/app.py
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, File, UploadFile
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import torch
|
| 4 |
+
import io
|
| 5 |
+
|
| 6 |
+
app = FastAPI(title="Car Damage Detection API")
|
| 7 |
+
|
| 8 |
+
# Load custom YOLOv8 model.
|
| 9 |
+
model = torch.hub.load('ultralytics/yolov8', 'custom', path='best.pt')
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@app.get("/")
|
| 13 |
+
def read_root():
|
| 14 |
+
""" A simple endpoint to check if the server is running. """
|
| 15 |
+
return {"status": "ok", "message": "Car Damage Detection API is running!"}
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@app.post("/detect")
|
| 19 |
+
async def detect_damage(file: UploadFile = File(...)):
|
| 20 |
+
"""
|
| 21 |
+
This endpoint receives an image, runs inference, and returns detections.
|
| 22 |
+
"""
|
| 23 |
+
# Read image content from the uploaded file
|
| 24 |
+
contents = await file.read()
|
| 25 |
+
image = Image.open(io.BytesIO(contents)).convert("RGB")
|
| 26 |
+
|
| 27 |
+
# Run the model on the image
|
| 28 |
+
results = model(image)
|
| 29 |
+
|
| 30 |
+
# Extract results into a clean format
|
| 31 |
+
predictions_df = results.pandas().xyxy[0]
|
| 32 |
+
|
| 33 |
+
# Filter detections by a confidence threshold (e.g., > 50%)
|
| 34 |
+
detections = predictions_df[predictions_df['confidence'] > 0.5]
|
| 35 |
+
|
| 36 |
+
# Convert the results to a JSON-friendly list of dictionaries
|
| 37 |
+
output = detections[['name', 'confidence']].to_dict(orient='records')
|
| 38 |
+
|
| 39 |
+
return {"detections": output}
|
best.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e5b520423c03c31efe05a1b5c9b9387b2a15a8ecb177c47e7d6e3a5bad01fb57
|
| 3 |
+
size 6250474
|
index.html
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Car Damage Detector</title>
|
| 7 |
+
<style>
|
| 8 |
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; text-align: center; margin: 0; background-color: #f0f2f5; }
|
| 9 |
+
.container { max-width: 600px; margin: 50px auto; padding: 30px; background-color: #fff; box-shadow: 0 4px 12px rgba(0,0,0,0.1); border-radius: 8px; }
|
| 10 |
+
h1 { color: #333; }
|
| 11 |
+
#image-preview { display: none; max-width: 100%; height: auto; margin-top: 20px; border-radius: 4px; border: 1px solid #ddd; }
|
| 12 |
+
.upload-area { border: 2px dashed #007bff; border-radius: 8px; padding: 40px; margin: 20px 0; cursor: pointer; background-color: #f8f9fa; }
|
| 13 |
+
.upload-area p { margin: 0; color: #555; }
|
| 14 |
+
button { font-size: 1.1em; padding: 12px 25px; cursor: pointer; background-color: #007bff; color: white; border: none; border-radius: 5px; transition: background-color 0.3s; }
|
| 15 |
+
button:hover { background-color: #0056b3; }
|
| 16 |
+
button:disabled { background-color: #aaa; cursor: not-allowed; }
|
| 17 |
+
#results { margin-top: 30px; font-size: 1.1em; text-align: left; }
|
| 18 |
+
#results ul { list-style-type: none; padding: 0; }
|
| 19 |
+
#results li { background-color: #e9ecef; padding: 10px; margin-bottom: 8px; border-radius: 4px; }
|
| 20 |
+
#results .confidence { color: #28a745; font-weight: bold; }
|
| 21 |
+
</style>
|
| 22 |
+
</head>
|
| 23 |
+
<body>
|
| 24 |
+
|
| 25 |
+
<div class="container">
|
| 26 |
+
<h1>🚗 Car Damage Detector</h1>
|
| 27 |
+
<p>Upload a photo to automatically check for scratches, dents, or rust.</p>
|
| 28 |
+
|
| 29 |
+
<div class="upload-area" id="upload-area">
|
| 30 |
+
<p>Click here or drag & drop an image</p>
|
| 31 |
+
</div>
|
| 32 |
+
|
| 33 |
+
<input type="file" id="image-upload" accept="image/*" hidden>
|
| 34 |
+
|
| 35 |
+
<button id="check-button" disabled>Check for Damage</button>
|
| 36 |
+
|
| 37 |
+
<img id="image-preview" src="" alt="Your uploaded image">
|
| 38 |
+
|
| 39 |
+
<div id="results"></div>
|
| 40 |
+
</div>
|
| 41 |
+
|
| 42 |
+
<script>
|
| 43 |
+
// ⚠️ IMPORTANT: REPLACE THIS URL with your actual Hugging Face Space URL!
|
| 44 |
+
const API_URL = "https://YOUR_USERNAME-YOUR_SPACE_NAME.hf.space/detect";
|
| 45 |
+
|
| 46 |
+
const uploadArea = document.getElementById('upload-area');
|
| 47 |
+
const imageUpload = document.getElementById('image-upload');
|
| 48 |
+
const checkButton = document.getElementById('check-button');
|
| 49 |
+
const imagePreview = document.getElementById('image-preview');
|
| 50 |
+
const resultsDiv = document.getElementById('results');
|
| 51 |
+
let selectedFile = null;
|
| 52 |
+
|
| 53 |
+
uploadArea.addEventListener('click', () => imageUpload.click());
|
| 54 |
+
imageUpload.addEventListener('change', handleFileSelect);
|
| 55 |
+
|
| 56 |
+
function handleFileSelect(event) {
|
| 57 |
+
selectedFile = event.target.files[0];
|
| 58 |
+
if (selectedFile) {
|
| 59 |
+
imagePreview.src = URL.createObjectURL(selectedFile);
|
| 60 |
+
imagePreview.style.display = 'block';
|
| 61 |
+
checkButton.disabled = false;
|
| 62 |
+
resultsDiv.innerHTML = '';
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
checkButton.addEventListener('click', async () => {
|
| 67 |
+
if (!selectedFile) {
|
| 68 |
+
alert("Please select an image first!");
|
| 69 |
+
return;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
resultsDiv.innerHTML = "<p>Analyzing, please wait... 🧐</p>";
|
| 73 |
+
checkButton.disabled = true;
|
| 74 |
+
checkButton.innerText = "Analyzing...";
|
| 75 |
+
|
| 76 |
+
const formData = new FormData();
|
| 77 |
+
formData.append('file', selectedFile);
|
| 78 |
+
|
| 79 |
+
try {
|
| 80 |
+
const response = await fetch(API_URL, {
|
| 81 |
+
method: 'POST',
|
| 82 |
+
body: formData,
|
| 83 |
+
});
|
| 84 |
+
|
| 85 |
+
if (!response.ok) {
|
| 86 |
+
throw new Error(`Server error: ${response.statusText}`);
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
const data = await response.json();
|
| 90 |
+
displayResults(data.detections);
|
| 91 |
+
|
| 92 |
+
} catch (error) {
|
| 93 |
+
resultsDiv.innerHTML = `<p style="color: red;">An error occurred: ${error.message}</p>`;
|
| 94 |
+
console.error("Error:", error);
|
| 95 |
+
} finally {
|
| 96 |
+
checkButton.disabled = false;
|
| 97 |
+
checkButton.innerText = "Check for Damage";
|
| 98 |
+
}
|
| 99 |
+
});
|
| 100 |
+
|
| 101 |
+
function displayResults(detections) {
|
| 102 |
+
if (detections.length === 0) {
|
| 103 |
+
resultsDiv.innerHTML = "<p>✅ No damage detected!</p>";
|
| 104 |
+
return;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
let html = "<h3>Detected Issues:</h3><ul>";
|
| 108 |
+
detections.forEach(det => {
|
| 109 |
+
const confidence = (det.confidence * 100).toFixed(1);
|
| 110 |
+
html += `<li><strong>${det.name}</strong> <span class="confidence">(${confidence}%)</span></li>`;
|
| 111 |
+
});
|
| 112 |
+
html += "</ul>";
|
| 113 |
+
resultsDiv.innerHTML = html;
|
| 114 |
+
}
|
| 115 |
+
</script>
|
| 116 |
+
|
| 117 |
+
</body>
|
| 118 |
+
</html>
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn
|
| 3 |
+
python-multipart
|
| 4 |
+
torch
|
| 5 |
+
ultralytics
|
| 6 |
+
Pillow
|