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> |