|
|
from flask import Flask, request, jsonify |
|
|
from detector.gesture_detector import detect_gesture |
|
|
from flask import send_from_directory |
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
|
@app.route("/detect", methods=["POST"]) |
|
|
def detect(): |
|
|
if "image" not in request.files: |
|
|
return jsonify({"error": "No image"}), 400 |
|
|
|
|
|
file = request.files["image"] |
|
|
result = detect_gesture(file) |
|
|
|
|
|
return jsonify({ |
|
|
|
|
|
"saved_to": result["saved_to"], |
|
|
"detections": result["detections"], |
|
|
"total_detections": result["total_detections"] |
|
|
}) |
|
|
|
|
|
@app.route('/detected_images/<path:filename>') |
|
|
def serve_detected_image(filename): |
|
|
return send_from_directory('/tmp/detected_images', filename) |
|
|
|
|
|
@app.route("/") |
|
|
def index(): |
|
|
return """ |
|
|
<!DOCTYPE html> |
|
|
<html> |
|
|
<head> |
|
|
<title>Gesture Detection</title> |
|
|
<style> |
|
|
body { |
|
|
font-family: 'Segoe UI', sans-serif; |
|
|
background: linear-gradient(to right, #eef2f3, #8e9eab); |
|
|
padding: 40px; |
|
|
max-width: 800px; |
|
|
margin: auto; |
|
|
} |
|
|
h1 { |
|
|
text-align: center; |
|
|
color: #333; |
|
|
} |
|
|
form, .nav-buttons { |
|
|
background-color: #fff; |
|
|
padding: 25px; |
|
|
border-radius: 15px; |
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1); |
|
|
margin-bottom: 25px; |
|
|
} |
|
|
input[type="file"] { |
|
|
display: block; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
button { |
|
|
padding: 10px 25px; |
|
|
font-size: 16px; |
|
|
background-color: #28a745; |
|
|
color: white; |
|
|
border: none; |
|
|
border-radius: 6px; |
|
|
cursor: pointer; |
|
|
transition: background-color 0.3s; |
|
|
} |
|
|
button:hover { |
|
|
background-color: #218838; |
|
|
} |
|
|
.result img { |
|
|
max-width: 100%; |
|
|
border-radius: 10px; |
|
|
margin-top: 15px; |
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.2); |
|
|
} |
|
|
.nav-buttons { |
|
|
text-align: center; |
|
|
} |
|
|
.nav-buttons a button { |
|
|
background-color: #007BFF; |
|
|
margin: 0 10px; |
|
|
} |
|
|
.nav-buttons a button:hover { |
|
|
background-color: #0056b3; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<h1>Image-Based Gesture Detection</h1> |
|
|
<form id="upload-form"> |
|
|
<label for="image"><strong>Upload an image:</strong></label> |
|
|
<input type="file" id="image" name="image" accept="image/*" required /> |
|
|
<button type="submit">Detect Gesture</button> |
|
|
</form> |
|
|
<div class="result" id="result"></div> |
|
|
<div class="nav-buttons"> |
|
|
<p><strong>Want real-time detection?</strong></p> |
|
|
<a href="/webcam"><button>Open Webcam</button></a> |
|
|
</div> |
|
|
<script> |
|
|
const form = document.getElementById("upload-form"); |
|
|
const resultDiv = document.getElementById("result"); |
|
|
form.addEventListener("submit", async (e) => { |
|
|
e.preventDefault(); |
|
|
const imageInput = document.getElementById("image"); |
|
|
const file = imageInput.files[0]; |
|
|
const formData = new FormData(); |
|
|
formData.append("image", file); |
|
|
resultDiv.innerHTML = "<p>Processing...</p>"; |
|
|
const response = await fetch("/detect", { |
|
|
method: "POST", |
|
|
body: formData |
|
|
}); |
|
|
if (!response.ok) { |
|
|
resultDiv.innerHTML = "<p style='color:red;'>Error detecting gesture.</p>"; |
|
|
return; |
|
|
} |
|
|
const data = await response.json(); |
|
|
resultDiv.innerHTML = ` |
|
|
<p><strong>Total Detections:</strong> ${data.total_detections}</p> |
|
|
<ul> |
|
|
${data.detections.map(d => `<li>${d.label} (${(d.confidence * 100).toFixed(1)}%)</li>`).join("")} |
|
|
</ul> |
|
|
<img src="${data.saved_to}" alt="Detected Image" /> |
|
|
`; |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
""" |
|
|
|
|
|
@app.route("/webcam") |
|
|
def webcam_ui(): |
|
|
return """ |
|
|
<!DOCTYPE html> |
|
|
<html> |
|
|
<head> |
|
|
<title>Live Gesture Detection</title> |
|
|
<style> |
|
|
body { |
|
|
font-family: 'Segoe UI', sans-serif; |
|
|
background: linear-gradient(to right, #e0eafc, #cfdef3); |
|
|
padding: 40px; |
|
|
max-width: 800px; |
|
|
margin: auto; |
|
|
text-align: center; |
|
|
} |
|
|
h2 { |
|
|
color: #333; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
video, canvas { |
|
|
width: 100%; |
|
|
max-width: 500px; |
|
|
border-radius: 10px; |
|
|
border: 2px solid #ccc; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
button { |
|
|
padding: 10px 20px; |
|
|
font-size: 16px; |
|
|
background-color: #007bff; |
|
|
color: white; |
|
|
border: none; |
|
|
border-radius: 6px; |
|
|
margin: 5px; |
|
|
cursor: pointer; |
|
|
transition: background-color 0.3s; |
|
|
} |
|
|
button:hover { |
|
|
background-color: #0056b3; |
|
|
} |
|
|
#result img { |
|
|
max-width: 100%; |
|
|
border-radius: 10px; |
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.2); |
|
|
margin-top: 10px; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<h2>Live Gesture Detection via Laptop Camera</h2> |
|
|
<video id="video" autoplay muted playsinline></video> |
|
|
<canvas id="canvas" style="display:none;"></canvas> |
|
|
<br> |
|
|
<button id="start">Start Detection</button> |
|
|
<button id="stop">Stop</button> |
|
|
<div id="result"></div> |
|
|
<script> |
|
|
const video = document.getElementById('video'); |
|
|
const canvas = document.getElementById('canvas'); |
|
|
const resultDiv = document.getElementById('result'); |
|
|
let intervalId = null; |
|
|
|
|
|
navigator.mediaDevices.getUserMedia({ video: true }) |
|
|
.then(stream => { |
|
|
video.srcObject = stream; |
|
|
}) |
|
|
.catch(err => { |
|
|
alert("Failed to access webcam: " + err); |
|
|
}); |
|
|
|
|
|
document.getElementById("start").onclick = () => { |
|
|
intervalId = setInterval(async () => { |
|
|
canvas.width = video.videoWidth; |
|
|
canvas.height = video.videoHeight; |
|
|
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height); |
|
|
canvas.toBlob(async (blob) => { |
|
|
const formData = new FormData(); |
|
|
formData.append("image", blob, "frame.jpg"); |
|
|
const res = await fetch("/detect", { |
|
|
method: "POST", |
|
|
body: formData |
|
|
}); |
|
|
if (res.ok) { |
|
|
const data = await res.json(); |
|
|
resultDiv.innerHTML = ` |
|
|
<p><strong>Total Detections:</strong> ${data.total_detections}</p> |
|
|
<ul> |
|
|
${data.detections.map(d => `<li>${d.label} (${(d.confidence * 100).toFixed(1)}%)</li>`).join("")} |
|
|
</ul> |
|
|
<img src="${data.saved_to}" /> |
|
|
`; |
|
|
} else { |
|
|
resultDiv.innerHTML = "<p style='color:red;'>Detection failed.</p>"; |
|
|
} |
|
|
}, "image/jpeg"); |
|
|
}, 1500); |
|
|
}; |
|
|
|
|
|
document.getElementById("stop").onclick = () => { |
|
|
clearInterval(intervalId); |
|
|
resultDiv.innerHTML = "<p style='color:gray;'>Stopped.</p>"; |
|
|
}; |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
""" |
|
|
|
|
|
if __name__ == "__main__": |
|
|
app.run(host="0.0.0.0", port=5000) |
|
|
|