Spaces:
Sleeping
Sleeping
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Real-Time Image Processing API Documentation</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.18.3/swagger-ui.min.css"> | |
<style> | |
body { | |
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; | |
margin: 0; | |
padding: 0; | |
color: #333; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 20px; | |
} | |
header { | |
background-color: #252f3f; | |
color: white; | |
padding: 20px; | |
box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
} | |
header h1 { | |
margin: 0; | |
font-size: 2em; | |
} | |
header p { | |
margin-top: 10px; | |
opacity: 0.8; | |
} | |
.endpoint-cards { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 20px; | |
margin-top: 30px; | |
} | |
.endpoint-card { | |
background-color: white; | |
border-radius: 8px; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
padding: 20px; | |
flex: 1 1 300px; | |
transition: transform 0.2s, box-shadow 0.2s; | |
} | |
.endpoint-card:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 5px 15px rgba(0,0,0,0.15); | |
} | |
.http-endpoint { | |
border-left: 5px solid #38a169; | |
} | |
.ws-endpoint { | |
border-left: 5px solid #3182ce; | |
} | |
.method { | |
display: inline-block; | |
padding: 5px 10px; | |
border-radius: 4px; | |
font-weight: bold; | |
margin-right: 10px; | |
} | |
.post { | |
background-color: #38a169; | |
color: white; | |
} | |
.ws { | |
background-color: #3182ce; | |
color: white; | |
} | |
.endpoint-title { | |
font-size: 1.2em; | |
margin-bottom: 15px; | |
display: flex; | |
align-items: center; | |
} | |
.section { | |
margin-top: 40px; | |
margin-bottom: 30px; | |
} | |
h2 { | |
border-bottom: 1px solid #eee; | |
padding-bottom: 10px; | |
color: #252f3f; | |
} | |
.code { | |
background-color: #f7fafc; | |
border-radius: 4px; | |
padding: 15px; | |
font-family: monospace; | |
overflow-x: auto; | |
margin: 15px 0; | |
} | |
.parameter-table, .response-table { | |
width: 100%; | |
border-collapse: collapse; | |
margin: 15px 0; | |
} | |
.parameter-table th, .parameter-table td, | |
.response-table th, .response-table td { | |
text-align: left; | |
padding: 10px; | |
border-bottom: 1px solid #eee; | |
} | |
.parameter-table th, .response-table th { | |
background-color: #f7fafc; | |
} | |
.try-it { | |
margin-top: 30px; | |
padding-top: 30px; | |
border-top: 1px solid #eee; | |
} | |
.swagger-ui .wrapper { max-width: 100%; } | |
#swagger-ui { | |
margin-top: 30px; | |
border: 1px solid #eee; | |
border-radius: 8px; | |
overflow: hidden; | |
} | |
.info-box { | |
background-color: #ebf8ff; | |
border-left: 5px solid #3182ce; | |
padding: 15px; | |
margin: 20px 0; | |
border-radius: 4px; | |
} | |
.response-example { | |
background-color: #f0fff4; | |
border-left: 5px solid #38a169; | |
padding: 15px; | |
border-radius: 4px; | |
margin-top: 20px; | |
} | |
/* Updated tabs styles */ | |
.tabs { | |
display: flex; | |
margin-top: 30px; | |
border-bottom: 1px solid #e2e8f0; | |
align-items: center; | |
} | |
.tab { | |
padding: 10px 20px; | |
cursor: pointer; | |
border-bottom: 2px solid transparent; | |
transition: all 0.2s ease; | |
} | |
.tab.active { | |
border-bottom: 2px solid #3182ce; | |
color: #3182ce; | |
font-weight: bold; | |
} | |
/* New try-button styles */ | |
.try-button { | |
margin-left: auto; | |
display: flex; | |
align-items: center; | |
padding: 10px 15px; | |
font-size: 14px; | |
font-weight: bold; | |
border-radius: 4px; | |
background-color: #4299e1; | |
color: white; | |
text-decoration: none; | |
border: none; | |
transition: background-color 0.2s; | |
} | |
.try-button:hover { | |
background-color: #3182ce; | |
} | |
.try-button svg { | |
margin-left: 8px; | |
height: 16px; | |
width: 16px; | |
} | |
.tab-content { | |
display: none; | |
padding: 20px 0; | |
} | |
.tab-content.active { | |
display: block; | |
} | |
button { | |
background-color: #4299e1; | |
color: white; | |
border: none; | |
padding: 10px 15px; | |
border-radius: 4px; | |
cursor: pointer; | |
font-weight: bold; | |
transition: background-color 0.2s; | |
} | |
button:hover { | |
background-color: #3182ce; | |
} | |
</style> | |
</head> | |
<body> | |
<header> | |
<div class="container"> | |
<h1>Advanced Driver Vision Assistance with Near Collision Estimation System</h1> | |
<p>API for object detection, depth estimation, and distance prediction using computer vision models</p> | |
</div> | |
</header> | |
<div class="container"> | |
<section class="section"> | |
<h2>Overview</h2> | |
<p>This API provides access to advanced computer vision models for real-time image processing. It leverages:</p> | |
<ul> | |
<li><strong>DETR (DEtection TRansformer)</strong> - For accurate object detection</li> | |
<li><strong>GLPN (Global-Local Path Networks)</strong> - For depth estimation</li> | |
<li><strong>LSTM Model</strong> - For Z-location prediction</li> | |
</ul> | |
<p>The API supports both HTTP and WebSocket protocols:</p> | |
<div class="endpoint-cards"> | |
<div class="endpoint-card http-endpoint"> | |
<div class="endpoint-title"> | |
<span class="method post">POST</span> | |
<span>/api/predict</span> | |
</div> | |
<p>Process a single image via HTTP request</p> | |
</div> | |
<div class="endpoint-card ws-endpoint"> | |
<div class="endpoint-title"> | |
<span class="method ws">WS</span> | |
<span>/ws/predict</span> | |
</div> | |
<p>Stream images for real-time processing via WebSocket</p> | |
</div> | |
</div> | |
</section> | |
<div class="tabs"> | |
<div class="tab active" onclick="switchTab('http-api')">HTTP API</div> | |
<div class="tab" onclick="switchTab('websocket-api')">WebSocket API</div> | |
<div class="tab"> | |
<a href="./try_page" class="ml-2 inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200"> | |
Try it | |
<svg xmlns="http://www.w3.org/2000/svg" class="ml-1 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" /> | |
</svg> | |
</a> | |
</div> | |
</div> | |
<div id="http-api" class="tab-content active"> | |
<section class="section"> | |
<h2>HTTP API Reference</h2> | |
<h3>POST /api/predict</h3> | |
<p>Process a single image for object detection, depth estimation, and distance prediction.</p> | |
<h4>Request</h4> | |
<p><strong>Content-Type:</strong> multipart/form-data</p> | |
<table class="parameter-table"> | |
<tr> | |
<th>Parameter</th> | |
<th>Type</th> | |
<th>Required</th> | |
<th>Description</th> | |
</tr> | |
<tr> | |
<td>file</td> | |
<td>File</td> | |
<td>Yes</td> | |
<td>The image file to process (JPEG, PNG)</td> | |
</tr> | |
</table> | |
<h4>Request Example</h4> | |
<div class="code"> | |
# Python example using requests | |
import requests | |
url = "http://localhost:8000/api/predict" | |
files = {"file": open("image.jpg", "rb")} | |
response = requests.post(url, files=files) | |
data = response.json() | |
print(data) | |
</div> | |
<h4>Response</h4> | |
<p>Returns a JSON object containing:</p> | |
<table class="response-table"> | |
<tr> | |
<th>Field</th> | |
<th>Type</th> | |
<th>Description</th> | |
</tr> | |
<tr> | |
<td>objects</td> | |
<td>Array</td> | |
<td>Array of detected objects with their properties</td> | |
</tr> | |
<tr> | |
<td>objects[].class</td> | |
<td>String</td> | |
<td>Class of the detected object (e.g., 'car', 'person')</td> | |
</tr> | |
<tr> | |
<td>objects[].distance_estimated</td> | |
<td>Number</td> | |
<td>Estimated distance of the object</td> | |
</tr> | |
<tr> | |
<td>objects[].features</td> | |
<td>Object</td> | |
<td>Features used for prediction (bounding box, depth information)</td> | |
</tr> | |
<tr> | |
<td>frame_id</td> | |
<td>Number</td> | |
<td>ID of the processed frame (0 for HTTP requests)</td> | |
</tr> | |
<tr> | |
<td>timings</td> | |
<td>Object</td> | |
<td>Processing time metrics for each step</td> | |
</tr> | |
</table> | |
<div class="response-example"> | |
<h4>Response Example</h4> | |
<pre class="code"> | |
{ | |
"objects": [ | |
{ | |
"class": "car", | |
"distance_estimated": 15.42, | |
"features": { | |
"xmin": 120.5, | |
"ymin": 230.8, | |
"xmax": 350.2, | |
"ymax": 480.3, | |
"mean_depth": 0.75, | |
"depth_mean_trim": 0.72, | |
"depth_median": 0.71, | |
"width": 229.7, | |
"height": 249.5 | |
} | |
}, | |
{ | |
"class": "person", | |
"distance_estimated": 8.76, | |
"features": { | |
"xmin": 450.1, | |
"ymin": 200.4, | |
"xmax": 510.8, | |
"ymax": 380.2, | |
"mean_depth": 0.58, | |
"depth_mean_trim": 0.56, | |
"depth_median": 0.55, | |
"width": 60.7, | |
"height": 179.8 | |
} | |
} | |
], | |
"frame_id": 0, | |
"timings": { | |
"decode_time": 0.015, | |
"models_time": 0.452, | |
"process_time": 0.063, | |
"json_time": 0.021, | |
"total_time": 0.551 | |
} | |
} | |
</pre> | |
</div> | |
<h4>HTTP Status Codes</h4> | |
<table class="response-table"> | |
<tr> | |
<th>Status Code</th> | |
<th>Description</th> | |
</tr> | |
<tr> | |
<td>200</td> | |
<td>OK - Request was successful</td> | |
</tr> | |
<tr> | |
<td>400</td> | |
<td>Bad Request - Empty file or invalid format</td> | |
</tr> | |
<tr> | |
<td>500</td> | |
<td>Internal Server Error - Processing error</td> | |
</tr> | |
</table> | |
</section> | |
</div> | |
<div id="websocket-api" class="tab-content"> | |
<section class="section"> | |
<h2>WebSocket API Reference</h2> | |
<h3>WebSocket /ws/predict</h3> | |
<p> | |
Stream images for real-time processing and get instant results. | |
Ideal for video feeds and applications requiring continuous processing. | |
</p> | |
<div class="info-box"> | |
<p><strong>Note:</strong> WebSocket offers better performance for real-time applications. Use this endpoint for processing video feeds or when you need to process multiple images in rapid succession.</p> | |
</div> | |
<h4>Connection</h4> | |
<div class="code"> | |
# JavaScript example | |
const socket = new WebSocket('ws://localhost:8000/ws/predict'); | |
socket.onopen = function(e) { | |
console.log('Connection established'); | |
}; | |
socket.onmessage = function(event) { | |
const response = JSON.parse(event.data); | |
console.log('Received:', response); | |
}; | |
socket.onclose = function(event) { | |
console.log('Connection closed'); | |
}; | |
</div> | |
<h4>Sending Images</h4> | |
<p>Send binary image data directly over the WebSocket connection:</p> | |
<div class="code"> | |
// JavaScript example: Sending an image from canvas or file | |
function sendImageFromCanvas(canvas) { | |
canvas.toBlob(function(blob) { | |
const reader = new FileReader(); | |
reader.onload = function() { | |
socket.send(reader.result); | |
}; | |
reader.readAsArrayBuffer(blob); | |
}, 'image/jpeg'); | |
} | |
// Or from input file | |
fileInput.onchange = function() { | |
const file = this.files[0]; | |
const reader = new FileReader(); | |
reader.onload = function() { | |
socket.send(reader.result); | |
}; | |
reader.readAsArrayBuffer(file); | |
}; | |
</div> | |
<h4>Response Format</h4> | |
<p>The WebSocket API returns the same JSON structure as the HTTP API, with incrementing frame_id values.</p> | |
<div class="response-example"> | |
<h4>Response Example</h4> | |
<pre class="code"> | |
{ | |
"objects": [ | |
{ | |
"class": "car", | |
"distance_estimated": 14.86, | |
"features": { | |
"xmin": 125.3, | |
"ymin": 235.1, | |
"xmax": 355.7, | |
"ymax": 485.9, | |
"mean_depth": 0.77, | |
"depth_mean_trim": 0.74, | |
"depth_median": 0.73, | |
"width": 230.4, | |
"height": 250.8 | |
} | |
} | |
], | |
"frame_id": 42, | |
"timings": { | |
"decode_time": 0.014, | |
"models_time": 0.445, | |
"process_time": 0.061, | |
"json_time": 0.020, | |
"total_time": 0.540 | |
} | |
} | |
</pre> | |
</div> | |
</section> | |
</div> | |
<div id="try-it" class="tab-content"> | |
<section class="section"> | |
<h2>Try The API</h2> | |
<p>You can test the API directly using the interactive Swagger UI below:</p> | |
<div id="swagger-ui"></div> | |
<h3>Simple WebSocket Client</h3> | |
<p>Upload an image to test the WebSocket endpoint:</p> | |
<div> | |
<input type="file" id="wsFileInput" accept="image/*"> | |
<button id="connectWsButton">Connect to WebSocket</button> | |
<button id="disconnectWsButton" disabled>Disconnect</button> | |
</div> | |
<div> | |
<p><strong>Status: </strong><span id="wsStatus">Disconnected</span></p> | |
<p><strong>Last Response: </strong></p> | |
<pre id="wsResponse" class="code" style="max-height: 300px; overflow-y: auto;"></pre> | |
</div> | |
</section> | |
</div> | |
</div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.18.3/swagger-ui-bundle.js"></script> | |
<script> | |
// Handle tab switching | |
function switchTab(tabId) { | |
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active')); | |
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); | |
document.querySelector(`.tab[onclick="switchTab('${tabId}')"]`).classList.add('active'); | |
document.getElementById(tabId).classList.add('active'); | |
} | |
// Initialize Swagger UI | |
window.onload = function() { | |
SwaggerUIBundle({ | |
url: "/api/openapi.json", | |
dom_id: '#swagger-ui', | |
presets: [ | |
SwaggerUIBundle.presets.apis, | |
SwaggerUIBundle.SwaggerUIStandalonePreset | |
], | |
layout: "BaseLayout", | |
deepLinking: true | |
}); | |
// WebSocket client setup | |
let socket = null; | |
const connectButton = document.getElementById('connectWsButton'); | |
const disconnectButton = document.getElementById('disconnectWsButton'); | |
const fileInput = document.getElementById('wsFileInput'); | |
const statusElement = document.getElementById('wsStatus'); | |
const responseElement = document.getElementById('wsResponse'); | |
connectButton.addEventListener('click', () => { | |
if (socket) { | |
socket.close(); | |
} | |
const wsUrl = window.location.protocol === 'https:' | |
? `wss://${window.location.host}/ws/predict` | |
: `ws://${window.location.host}/ws/predict`; | |
socket = new WebSocket(wsUrl); | |
socket.onopen = function() { | |
statusElement.textContent = 'Connected'; | |
statusElement.style.color = 'green'; | |
connectButton.disabled = true; | |
disconnectButton.disabled = false; | |
}; | |
socket.onmessage = function(event) { | |
const response = JSON.parse(event.data); | |
responseElement.textContent = JSON.stringify(response, null, 2); | |
}; | |
socket.onclose = function() { | |
statusElement.textContent = 'Disconnected'; | |
statusElement.style.color = 'red'; | |
connectButton.disabled = false; | |
disconnectButton.disabled = true; | |
socket = null; | |
}; | |
socket.onerror = function(error) { | |
statusElement.textContent = 'Error: ' + error.message; | |
statusElement.style.color = 'red'; | |
}; | |
}); | |
disconnectButton.addEventListener('click', () => { | |
if (socket) { | |
socket.close(); | |
} | |
}); | |
fileInput.addEventListener('change', () => { | |
if (!socket || socket.readyState !== WebSocket.OPEN) { | |
alert('Please connect to WebSocket first'); | |
return; | |
} | |
const file = fileInput.files[0]; | |
if (!file) return; | |
const reader = new FileReader(); | |
reader.onload = function() { | |
socket.send(reader.result); | |
}; | |
reader.readAsArrayBuffer(file); | |
}); | |
}; | |
</script> | |
</body> | |
</html> |