From 811677029c60bd25bd375da512db415ba6c71cd1 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 20:42:28 +0200 Subject: [PATCH 001/279] Added flask web demo --- Dockerfile.flask | 20 ++++++ common/utils.py | 24 +++++++ docker-compose.yml | 18 ++++++ requirements.flask.txt | 0 src/web_demo/app.py | 96 ++++++++++++++++++++++++++++ src/web_demo/static/js/scripts.js | 103 ++++++++++++++++++++++++++++++ src/web_demo/templates/base.html | 17 +++++ src/web_demo/templates/demo.html | 51 +++++++++++++++ 8 files changed, 329 insertions(+) create mode 100644 Dockerfile.flask create mode 100644 docker-compose.yml create mode 100644 requirements.flask.txt create mode 100644 src/web_demo/app.py create mode 100644 src/web_demo/static/js/scripts.js create mode 100644 src/web_demo/templates/base.html create mode 100644 src/web_demo/templates/demo.html diff --git a/Dockerfile.flask b/Dockerfile.flask new file mode 100644 index 0000000..01006d1 --- /dev/null +++ b/Dockerfile.flask @@ -0,0 +1,20 @@ +# Use an official Python runtime as a parent image +FROM python:3.8-slim + +# Set the working directory in the container +WORKDIR /usr/src/app + +# Copy the Flask app requirements file into the container at /usr/src/app +COPY requirements.flask.txt ./ + +# Install any needed packages specified in requirements.flask.txt +RUN pip install --no-cache-dir -r requirements.flask.txt + +# Copy the Flask app directory into the container +COPY ./src/web ./src/web + +# Make port 5000 available to the world outside this container +EXPOSE 5000 + +# Define the command to run the Flask app +CMD ["python", "./src/web/app.py"] diff --git a/common/utils.py b/common/utils.py index a8e0a33..d103eb3 100644 --- a/common/utils.py +++ b/common/utils.py @@ -1,3 +1,5 @@ +import torch +import logging import argparse import pandas as pd import matplotlib.pyplot as plt @@ -111,3 +113,25 @@ def parse_arguments(): ) return parser.parse_args() + + +def cuda_is_available() -> bool: + """ + Check the availability of CUDA and TensorRT on the system. + + Determines if CUDA is available and if the 'torch_tensorrt' package is + installed. Logs a warning if 'torch_tensorrt' is not installed. + + :return: True if CUDA is available and 'torch_tensorrt' is installed, False otherwise. + """ + cuda_available = False + if torch.cuda.is_available(): + try: + import torch_tensorrt + + cuda_available = True + except ImportError: + logging.warning( + "torch-tensorrt is not installed. Running on CPU mode only." + ) + return cuda_available diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9fad0e1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3' + +services: + resnet_tensorrt: + build: + context: . + dockerfile: Dockerfile + ports: + - "8000:8000" + + flask_app: + build: + context: . + dockerfile: Dockerfile.flask + ports: + - "5000:5000" + depends_on: + - resnet_tensorrt diff --git a/requirements.flask.txt b/requirements.flask.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/web_demo/app.py b/src/web_demo/app.py new file mode 100644 index 0000000..dfdfa96 --- /dev/null +++ b/src/web_demo/app.py @@ -0,0 +1,96 @@ +from flask import Flask, render_template, request, jsonify +from PIL import Image +from io import BytesIO +from common.utils import cuda_is_available + +# Importing model and inference classes +from src.image_processor import ImageProcessor +from src.model import ModelLoader +from src.onnx_inference import ONNXInference +from src.ov_inference import OVInference +from src.pytorch_inference import PyTorchInference +from src.tensorrt_inference import TensorRTInference + +app = Flask(__name__) + + +def process_image(image_file): + image = Image.open(BytesIO(image_file.read())) + img_processor = ImageProcessor(device="cpu") + return img_processor.process_image(image) + + +def get_inference_class(model_type, model_loader): + if model_type == "pytorch": + return PyTorchInference(model_loader, device="cpu") + elif model_type == "onnx": + return ONNXInference(model_loader, "path_to_onnx_model") + elif model_type == "ov": + return OVInference(model_loader, "path_to_ov_model") + elif model_type == "tensorrt": + return TensorRTInference(model_loader, device="cpu") + elif model_type == "all": + return None # Placeholder for 'all' models + + +def run_all_benchmarks(img_batch): + model_loader = ModelLoader(device="cpu") + benchmark_results = {} + + # PyTorch CPU Benchmark + pytorch_cpu_inference = PyTorchInference(model_loader, device="cpu") + benchmark_results["PyTorch (CPU)"] = pytorch_cpu_inference.benchmark(img_batch) + + # PyTorch GPU Benchmark + if cuda_is_available(): + pytorch_gpu_inference = PyTorchInference(model_loader, device="cuda") + benchmark_results["PyTorch (GPU)"] = pytorch_gpu_inference.benchmark(img_batch) + + # ONNX CPU Benchmark + onnx_inference = ONNXInference(model_loader, "path_to_onnx_model") + benchmark_results["ONNX (CPU)"] = onnx_inference.benchmark(img_batch) + + # OpenVINO CPU Benchmark + ov_inference = OVInference(model_loader, "path_to_ov_model") + benchmark_results["OpenVINO (CPU)"] = ov_inference.benchmark(img_batch) + + # TensorRT CPU Benchmark + if cuda_is_available(): + tensorrt_inference = TensorRTInference(model_loader, device="cpu") + benchmark_results["TensorRT (CPU)"] = tensorrt_inference.benchmark(img_batch) + + return benchmark_results + + +@app.route("/demo") +def index(): + return render_template("demo.html") + + +@app.route("/process", methods=["POST"]) +def process_request(): + image_file = request.files.get("image") + model_type = request.form.get("model") + mode = request.form.get("mode") + + img_batch = process_image(image_file) + model_loader = ModelLoader(device="cpu") + + if mode == "benchmark" and model_type == "all": + results = run_all_benchmarks(img_batch) + return jsonify({"benchmark": results}) + + inference_class = get_inference_class(model_type, model_loader) + if inference_class is None: + return jsonify({"error": "Invalid model type selected"}), 400 + + if mode == "predict": + results = inference_class.predict(img_batch) + return jsonify({"predictions": results}) + elif mode == "benchmark": + results = inference_class.benchmark(img_batch) + return jsonify({"benchmark": results}) + + +if __name__ == "__main__": + app.run(debug=True, ssl_context="adhoc") diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js new file mode 100644 index 0000000..80e5335 --- /dev/null +++ b/src/web_demo/static/js/scripts.js @@ -0,0 +1,103 @@ +document.getElementById('image-form').addEventListener('submit', function(e) { + e.preventDefault(); + let formData = new FormData(this); + + document.getElementById('spinner').style.display = 'block'; + + fetch('/process', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + document.getElementById('spinner').style.display = 'none'; + if (data.predictions) { + displayPredictions(data.predictions); + } else if (data.benchmark) { + displayBenchmark(data.benchmark); + } + }) + .catch(error => { + console.error('Error:', error); + }); +}); + +function displayPredictions(predictions) { + const resultsDiv = document.getElementById('results'); + resultsDiv.innerHTML = ''; // Clear previous results + + predictions.forEach(prediction => { + const p = document.createElement('p'); + p.textContent = `Label: ${prediction.label}, Confidence: ${prediction.confidence.toFixed(2)}`; + resultsDiv.appendChild(p); + }); +} + + +function displayBenchmark(benchmarkResults) { + const resultsDiv = document.getElementById('results'); + resultsDiv.innerHTML = ''; // Clear previous results + + for (const model in benchmarkResults) { + const time = benchmarkResults[model].avgTime; + const throughput = benchmarkResults[model].avgThroughput; + + const p = document.createElement('p'); + p.textContent = `${model} - Average Time: ${time.toFixed(2)} ms, Throughput: ${throughput.toFixed(2)}`; + resultsDiv.appendChild(p); + } + + // If you have data for plotting (e.g., for 'ALL' mode), call displayLineGraph + if (benchmarkResults['all']) { + displayLineGraph(benchmarkResults['all']); + } +} + + +function displayLineGraph(data) { + document.getElementById('lineGraphContainer').style.display = 'block'; + + const ctx = document.getElementById('lineGraph').getContext('2d'); + const labels = Object.keys(data); + const times = labels.map(label => data[label].time); + const throughputs = labels.map(label => data[label].throughput); + + new Chart(ctx, { + type: 'line', + data: { + labels: labels, + datasets: [{ + label: 'Inference Time (ms)', + data: times, + backgroundColor: 'rgba(255, 99, 132, 0.2)', + borderColor: 'rgba(255, 99, 132, 1)', + borderWidth: 1, + yAxisID: 'y-axis-time', + }, { + label: 'Throughput', + data: throughputs, + backgroundColor: 'rgba(54, 162, 235, 0.2)', + borderColor: 'rgba(54, 162, 235, 1)', + borderWidth: 1, + yAxisID: 'y-axis-throughput', + }] + }, + options: { + scales: { + 'y-axis-time': { + type: 'linear', + display: true, + position: 'left', + }, + 'y-axis-throughput': { + type: 'linear', + display: true, + position: 'right', + grid: { + drawOnChartArea: false, + }, + } + } + } + }); +} diff --git a/src/web_demo/templates/base.html b/src/web_demo/templates/base.html new file mode 100644 index 0000000..e03f842 --- /dev/null +++ b/src/web_demo/templates/base.html @@ -0,0 +1,17 @@ + + + + {% block title %}{% endblock %} - ResNetTensorRT Demo + + {% block head %}{% endblock %} + + +
+ {% block content %}{% endblock %} +
+ + + + {% block scripts %}{% endblock %} + + \ No newline at end of file diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html new file mode 100644 index 0000000..81fe046 --- /dev/null +++ b/src/web_demo/templates/demo.html @@ -0,0 +1,51 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

ResNetTensorRT Image Processing Demo

+
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ +
+ +
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} From c449c8b5fc14d1e4dab2a30ee441b68b974ea78d Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 20:52:33 +0200 Subject: [PATCH 002/279] Fix Docker COPY path for Flask app --- Dockerfile.flask | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.flask b/Dockerfile.flask index 01006d1..b9f6b4b 100644 --- a/Dockerfile.flask +++ b/Dockerfile.flask @@ -11,7 +11,7 @@ COPY requirements.flask.txt ./ RUN pip install --no-cache-dir -r requirements.flask.txt # Copy the Flask app directory into the container -COPY ./src/web ./src/web +COPY ./src/web /usr/src/app/src/web # Make port 5000 available to the world outside this container EXPOSE 5000 From efadd420e19a1555d17858d22916a39b547732e0 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 20:54:51 +0200 Subject: [PATCH 003/279] Fix Docker COPY path for Flask app --- Dockerfile.flask | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.flask b/Dockerfile.flask index b9f6b4b..5ff48b8 100644 --- a/Dockerfile.flask +++ b/Dockerfile.flask @@ -11,7 +11,7 @@ COPY requirements.flask.txt ./ RUN pip install --no-cache-dir -r requirements.flask.txt # Copy the Flask app directory into the container -COPY ./src/web /usr/src/app/src/web +COPY ./src/web_demo ./src/web_demo # Make port 5000 available to the world outside this container EXPOSE 5000 From ee43f044fa075ff63aa0a93e7c0c19465a6e45bc Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 20:55:27 +0200 Subject: [PATCH 004/279] Fix Docker COPY path for Flask app --- Dockerfile.flask | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.flask b/Dockerfile.flask index 5ff48b8..377db23 100644 --- a/Dockerfile.flask +++ b/Dockerfile.flask @@ -17,4 +17,4 @@ COPY ./src/web_demo ./src/web_demo EXPOSE 5000 # Define the command to run the Flask app -CMD ["python", "./src/web/app.py"] +CMD ["python", "./src/web_demo/app.py"] From 8c6ef39949567bba64f7553820ebd6e19f711dea Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 20:57:08 +0200 Subject: [PATCH 005/279] Updated requirements.flask.txt --- requirements.flask.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.flask.txt b/requirements.flask.txt index e69de29..feb576f 100644 --- a/requirements.flask.txt +++ b/requirements.flask.txt @@ -0,0 +1,3 @@ +Flask +gunicorn +requests \ No newline at end of file From d756aba45933ac461626918402cea1a74866081d Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 20:57:48 +0200 Subject: [PATCH 006/279] Updated requirements.flask.txt --- requirements.flask.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.flask.txt b/requirements.flask.txt index feb576f..b1b930b 100644 --- a/requirements.flask.txt +++ b/requirements.flask.txt @@ -1,3 +1,4 @@ Flask gunicorn -requests \ No newline at end of file +requests +PIL \ No newline at end of file From 023bc8466bfec2db93d72ba1f68f6cf627bc82df Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 20:59:13 +0200 Subject: [PATCH 007/279] Updated requirements.flask.txt --- requirements.flask.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.flask.txt b/requirements.flask.txt index b1b930b..3323009 100644 --- a/requirements.flask.txt +++ b/requirements.flask.txt @@ -1,4 +1,4 @@ Flask gunicorn requests -PIL \ No newline at end of file +Pillow \ No newline at end of file From 64e91dfdc88dfff02b79f41fafe0adcddb40b0ca Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 21:12:23 +0200 Subject: [PATCH 008/279] Adjust the path for web app includes --- src/web_demo/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index dfdfa96..fdc0cdc 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,6 +1,8 @@ from flask import Flask, render_template, request, jsonify from PIL import Image from io import BytesIO +import sys +sys.path.append('../../') from common.utils import cuda_is_available # Importing model and inference classes From 43c9e1676272edbf5bfc0b40467b9db565a4ac80 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 21:17:51 +0200 Subject: [PATCH 009/279] Adjust the path for web app includes --- Dockerfile.flask | 2 ++ src/web_demo/app.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile.flask b/Dockerfile.flask index 377db23..b7f7a26 100644 --- a/Dockerfile.flask +++ b/Dockerfile.flask @@ -12,6 +12,8 @@ RUN pip install --no-cache-dir -r requirements.flask.txt # Copy the Flask app directory into the container COPY ./src/web_demo ./src/web_demo +COPY ./common /usr/src/app/common +COPY ./inference /usr/src/app/inference # Make port 5000 available to the world outside this container EXPOSE 5000 diff --git a/src/web_demo/app.py b/src/web_demo/app.py index fdc0cdc..46de9e8 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -26,9 +26,9 @@ def get_inference_class(model_type, model_loader): if model_type == "pytorch": return PyTorchInference(model_loader, device="cpu") elif model_type == "onnx": - return ONNXInference(model_loader, "path_to_onnx_model") + return ONNXInference(model_loader, "./models/model.onnx") elif model_type == "ov": - return OVInference(model_loader, "path_to_ov_model") + return OVInference(model_loader, "./models/model.ov") elif model_type == "tensorrt": return TensorRTInference(model_loader, device="cpu") elif model_type == "all": From becc4054a2509995d620bf8941d328938894463a Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 21:19:41 +0200 Subject: [PATCH 010/279] Adjust the path for web app includes --- src/web_demo/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 46de9e8..2efab8c 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -2,7 +2,7 @@ from PIL import Image from io import BytesIO import sys -sys.path.append('../../') +sys.path.append('/usr/src/app') from common.utils import cuda_is_available # Importing model and inference classes From a99ff7b4d1d370a8858c79cd99b393581fa982e5 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 21:20:32 +0200 Subject: [PATCH 011/279] Adjust the path for web app includes --- requirements.flask.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.flask.txt b/requirements.flask.txt index 3323009..3341e1e 100644 --- a/requirements.flask.txt +++ b/requirements.flask.txt @@ -1,3 +1,4 @@ +torch Flask gunicorn requests From de4e4b05b3d730326d278a37db2c0ece0ec57611 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 21:28:26 +0200 Subject: [PATCH 012/279] Adjust the path for web app includes --- requirements.flask.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.flask.txt b/requirements.flask.txt index 3341e1e..012dc2a 100644 --- a/requirements.flask.txt +++ b/requirements.flask.txt @@ -2,4 +2,5 @@ torch Flask gunicorn requests -Pillow \ No newline at end of file +Pillow +pandas \ No newline at end of file From e0244b8f8678b3b26e6498df4bb585d490e0f57b Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 21:35:57 +0200 Subject: [PATCH 013/279] Adjust the path for web app includes --- Dockerfile.flask | 2 +- requirements.flask.txt | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Dockerfile.flask b/Dockerfile.flask index b7f7a26..13c88af 100644 --- a/Dockerfile.flask +++ b/Dockerfile.flask @@ -8,7 +8,7 @@ WORKDIR /usr/src/app COPY requirements.flask.txt ./ # Install any needed packages specified in requirements.flask.txt -RUN pip install --no-cache-dir -r requirements.flask.txt +RUN pip install -r requirements.flask.txt # Copy the Flask app directory into the container COPY ./src/web_demo ./src/web_demo diff --git a/requirements.flask.txt b/requirements.flask.txt index 012dc2a..7e5e43a 100644 --- a/requirements.flask.txt +++ b/requirements.flask.txt @@ -3,4 +3,13 @@ Flask gunicorn requests Pillow -pandas \ No newline at end of file +pandas +torchvision +pandas +numpy +packaging +onnx +onnxruntime +openvino==2023.1.0.dev20230811 +seaborn +matplotlib \ No newline at end of file From 34dbc71dc1c67a3f64c7171d51ef249833e5f2da Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 21:55:06 +0200 Subject: [PATCH 014/279] Adjust the path for web app includes --- Dockerfile.flask | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.flask b/Dockerfile.flask index 13c88af..ecfad6f 100644 --- a/Dockerfile.flask +++ b/Dockerfile.flask @@ -11,7 +11,7 @@ COPY requirements.flask.txt ./ RUN pip install -r requirements.flask.txt # Copy the Flask app directory into the container -COPY ./src/web_demo ./src/web_demo +COPY ./src /usr/src/app/src COPY ./common /usr/src/app/common COPY ./inference /usr/src/app/inference From 8b2efbe96dd92d427c1a5a8c4d3dc9a0567ff2cc Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 22:00:18 +0200 Subject: [PATCH 015/279] Adjust the path for web app includes --- requirements.flask.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.flask.txt b/requirements.flask.txt index 7e5e43a..75042f1 100644 --- a/requirements.flask.txt +++ b/requirements.flask.txt @@ -12,4 +12,5 @@ onnx onnxruntime openvino==2023.1.0.dev20230811 seaborn -matplotlib \ No newline at end of file +matplotlib +cryptography \ No newline at end of file From b22ecba6a9c874238bac76023a411fea9933d353 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 22:10:16 +0200 Subject: [PATCH 016/279] Disabled the SSL --- src/web_demo/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 2efab8c..0530fd6 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -95,4 +95,4 @@ def process_request(): if __name__ == "__main__": - app.run(debug=True, ssl_context="adhoc") + app.run(debug=True) From 7e0249d325befdd022bb9363fd8ef97f3d77ad7c Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 22:26:18 +0200 Subject: [PATCH 017/279] Added the SSL keys directory --- Dockerfile.flask | 2 +- docker-compose.yml | 2 ++ requirements.flask.txt | 3 +-- src/web_demo/config.py | 2 ++ 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 src/web_demo/config.py diff --git a/Dockerfile.flask b/Dockerfile.flask index ecfad6f..ddf4c6f 100644 --- a/Dockerfile.flask +++ b/Dockerfile.flask @@ -10,10 +10,10 @@ COPY requirements.flask.txt ./ # Install any needed packages specified in requirements.flask.txt RUN pip install -r requirements.flask.txt -# Copy the Flask app directory into the container COPY ./src /usr/src/app/src COPY ./common /usr/src/app/common COPY ./inference /usr/src/app/inference +COPY ./keys /usr/src/app/keys # Make port 5000 available to the world outside this container EXPOSE 5000 diff --git a/docker-compose.yml b/docker-compose.yml index 9fad0e1..d611282 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,5 +14,7 @@ services: dockerfile: Dockerfile.flask ports: - "5000:5000" + volumes: + - ./keys:/usr/src/app/keys depends_on: - resnet_tensorrt diff --git a/requirements.flask.txt b/requirements.flask.txt index 75042f1..7e5e43a 100644 --- a/requirements.flask.txt +++ b/requirements.flask.txt @@ -12,5 +12,4 @@ onnx onnxruntime openvino==2023.1.0.dev20230811 seaborn -matplotlib -cryptography \ No newline at end of file +matplotlib \ No newline at end of file diff --git a/src/web_demo/config.py b/src/web_demo/config.py new file mode 100644 index 0000000..1bf7f67 --- /dev/null +++ b/src/web_demo/config.py @@ -0,0 +1,2 @@ +SSL_CERT_PATH = "/usr/src/app/keys/certificate.crt" +SSL_KEY_PATH = "/usr/src/app/keys/private.key" \ No newline at end of file From fa777136067750712fb628c1f9669d82123438f6 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 22:31:53 +0200 Subject: [PATCH 018/279] Added empty directory --- keys/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 keys/.gitkeep diff --git a/keys/.gitkeep b/keys/.gitkeep new file mode 100644 index 0000000..e69de29 From 642cbb9c60dd3b01fb0644be1938acde88e951ca Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 22:37:15 +0200 Subject: [PATCH 019/279] Added SSL key directory --- src/web_demo/app.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 0530fd6..ce93371 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,6 +1,8 @@ from flask import Flask, render_template, request, jsonify from PIL import Image from io import BytesIO +from config import SSL_CERT_PATH, SSL_KEY_PATH + import sys sys.path.append('/usr/src/app') from common.utils import cuda_is_available @@ -95,4 +97,4 @@ def process_request(): if __name__ == "__main__": - app.run(debug=True) + app.run(host="0.0.0.0", port=5000, ssl_context=(SSL_CERT_PATH, SSL_KEY_PATH), debug=True) From e9e0078557ad12e5b047babd937903471f0e5e24 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 22:40:35 +0200 Subject: [PATCH 020/279] Fixed typo in c ertificate name --- src/web_demo/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/config.py b/src/web_demo/config.py index 1bf7f67..ab8bdc0 100644 --- a/src/web_demo/config.py +++ b/src/web_demo/config.py @@ -1,2 +1,2 @@ -SSL_CERT_PATH = "/usr/src/app/keys/certificate.crt" +SSL_CERT_PATH = "/usr/src/app/keys/certificate.csr" SSL_KEY_PATH = "/usr/src/app/keys/private.key" \ No newline at end of file From 9c2281b81aa18c8ffe7f01190e71509b8ee416e4 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 23:01:50 +0200 Subject: [PATCH 021/279] Fixed typo in c ertificate name --- src/web_demo/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index ce93371..8fab137 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -97,4 +97,4 @@ def process_request(): if __name__ == "__main__": - app.run(host="0.0.0.0", port=5000, ssl_context=(SSL_CERT_PATH, SSL_KEY_PATH), debug=True) + app.run(host="0.0.0.0", port=5000, debug=True) From 60261e0a1a727797495cd7f92b160a6cab247ff9 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 23:02:17 +0200 Subject: [PATCH 022/279] Fixed typo in c ertificate name --- Dockerfile.flask | 1 - keys/.gitkeep | 0 2 files changed, 1 deletion(-) delete mode 100644 keys/.gitkeep diff --git a/Dockerfile.flask b/Dockerfile.flask index ddf4c6f..badac3c 100644 --- a/Dockerfile.flask +++ b/Dockerfile.flask @@ -13,7 +13,6 @@ RUN pip install -r requirements.flask.txt COPY ./src /usr/src/app/src COPY ./common /usr/src/app/common COPY ./inference /usr/src/app/inference -COPY ./keys /usr/src/app/keys # Make port 5000 available to the world outside this container EXPOSE 5000 diff --git a/keys/.gitkeep b/keys/.gitkeep deleted file mode 100644 index e69de29..0000000 From 2b74c4441b805283def8738ad706a1765f87f79a Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 23:27:07 +0200 Subject: [PATCH 023/279] Changing port to 8080 --- src/web_demo/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 8fab137..b79bffb 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -97,4 +97,4 @@ def process_request(): if __name__ == "__main__": - app.run(host="0.0.0.0", port=5000, debug=True) + app.run(host="0.0.0.0", port=8080, debug=True) From 9985c698aef869881d074548cbf303e42767d8a6 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 23:28:57 +0200 Subject: [PATCH 024/279] revert Changing port to 8080 --- src/web_demo/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index b79bffb..8fab137 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -97,4 +97,4 @@ def process_request(): if __name__ == "__main__": - app.run(host="0.0.0.0", port=8080, debug=True) + app.run(host="0.0.0.0", port=5000, debug=True) From 1e6d097a5ec8991416f8bce0bc513fd48562d8f6 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 23:31:50 +0200 Subject: [PATCH 025/279] Adding SSL keys again --- Dockerfile.flask | 1 + src/web_demo/app.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile.flask b/Dockerfile.flask index badac3c..ddf4c6f 100644 --- a/Dockerfile.flask +++ b/Dockerfile.flask @@ -13,6 +13,7 @@ RUN pip install -r requirements.flask.txt COPY ./src /usr/src/app/src COPY ./common /usr/src/app/common COPY ./inference /usr/src/app/inference +COPY ./keys /usr/src/app/keys # Make port 5000 available to the world outside this container EXPOSE 5000 diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 8fab137..38d8498 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -97,4 +97,4 @@ def process_request(): if __name__ == "__main__": - app.run(host="0.0.0.0", port=5000, debug=True) + app.run(host="0.0.0.0", port=5000,ssl_context=(SSL_CERT_PATH, SSL_KEY_PATH), debug=True) From 46c2db44ae0ccb2dfcac15b808a55261535f1b0a Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 23:33:22 +0200 Subject: [PATCH 026/279] Adding SSL keys again --- src/web_demo/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/config.py b/src/web_demo/config.py index ab8bdc0..1bf7f67 100644 --- a/src/web_demo/config.py +++ b/src/web_demo/config.py @@ -1,2 +1,2 @@ -SSL_CERT_PATH = "/usr/src/app/keys/certificate.csr" +SSL_CERT_PATH = "/usr/src/app/keys/certificate.crt" SSL_KEY_PATH = "/usr/src/app/keys/private.key" \ No newline at end of file From 398620b7fe7227462548f0f38938ca917e291e80 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 23:40:32 +0200 Subject: [PATCH 027/279] Adding SSL keys again --- src/web_demo/app.py | 18 ++++++++++++++++-- src/web_demo/config.py | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 38d8498..6e76721 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,10 +1,13 @@ +import logging + from flask import Flask, render_template, request, jsonify from PIL import Image from io import BytesIO from config import SSL_CERT_PATH, SSL_KEY_PATH import sys -sys.path.append('/usr/src/app') + +sys.path.append("/usr/src/app") from common.utils import cuda_is_available # Importing model and inference classes @@ -77,24 +80,35 @@ def process_request(): model_type = request.form.get("model") mode = request.form.get("mode") + # Add logging statements + logging.info("Received request with model_type: %s and mode: %s", model_type, mode) + img_batch = process_image(image_file) model_loader = ModelLoader(device="cpu") if mode == "benchmark" and model_type == "all": + logging.info("Running all benchmarks") results = run_all_benchmarks(img_batch) return jsonify({"benchmark": results}) inference_class = get_inference_class(model_type, model_loader) if inference_class is None: + logging.error("Invalid model type selected: %s", model_type) return jsonify({"error": "Invalid model type selected"}), 400 if mode == "predict": + logging.info("Running prediction") results = inference_class.predict(img_batch) return jsonify({"predictions": results}) elif mode == "benchmark": + logging.info("Running benchmark") results = inference_class.benchmark(img_batch) return jsonify({"benchmark": results}) if __name__ == "__main__": - app.run(host="0.0.0.0", port=5000,ssl_context=(SSL_CERT_PATH, SSL_KEY_PATH), debug=True) + # Configure logging + logging.basicConfig(level=logging.INFO) + app.run( + host="0.0.0.0", port=5000, ssl_context=(SSL_CERT_PATH, SSL_KEY_PATH), debug=True + ) diff --git a/src/web_demo/config.py b/src/web_demo/config.py index 1bf7f67..a29f39f 100644 --- a/src/web_demo/config.py +++ b/src/web_demo/config.py @@ -1,2 +1,2 @@ SSL_CERT_PATH = "/usr/src/app/keys/certificate.crt" -SSL_KEY_PATH = "/usr/src/app/keys/private.key" \ No newline at end of file +SSL_KEY_PATH = "/usr/src/app/keys/private.key" From 0b595f2f534d35c28fcdb3232c222d0373e33c6b Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Fri, 22 Dec 2023 23:42:50 +0200 Subject: [PATCH 028/279] Adding SSL keys again --- src/web_demo/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 6e76721..793684a 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -83,7 +83,10 @@ def process_request(): # Add logging statements logging.info("Received request with model_type: %s and mode: %s", model_type, mode) + logging.info("Processing image") img_batch = process_image(image_file) + + logging.info("Loading pre-trained model") model_loader = ModelLoader(device="cpu") if mode == "benchmark" and model_type == "all": From 2db27229acd44d70934a538b3a77b90308d4298f Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:04:49 +0200 Subject: [PATCH 029/279] Added file upload support and limits --- requirements.flask.txt | 4 ++- src/web_demo/app.py | 82 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/requirements.flask.txt b/requirements.flask.txt index 7e5e43a..5c6aaa7 100644 --- a/requirements.flask.txt +++ b/requirements.flask.txt @@ -12,4 +12,6 @@ onnx onnxruntime openvino==2023.1.0.dev20230811 seaborn -matplotlib \ No newline at end of file +matplotlib +Flask-Limiter==1.5.0 +Werkzeug==2.0.2 \ No newline at end of file diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 793684a..0091199 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,9 +1,14 @@ import logging -from flask import Flask, render_template, request, jsonify +from flask import Flask, render_template, request, jsonify, flash, redirect, url_for from PIL import Image from io import BytesIO from config import SSL_CERT_PATH, SSL_KEY_PATH +from werkzeug.exceptions import RequestEntityTooLarge +from flask_limiter.util import get_remote_address +from flask_limiter import Limiter +import os +import uuid import sys @@ -21,12 +26,48 @@ app = Flask(__name__) -def process_image(image_file): - image = Image.open(BytesIO(image_file.read())) +UPLOAD_FOLDER = "static/user_files" +ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif"} +MAX_CONTENT_LENGTH = 500 * 1024 * 1024 # 500MB +MAX_FILES_IN_UPLOAD_FOLDER = 10 + +app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER +app.config["MAX_CONTENT_LENGTH"] = MAX_CONTENT_LENGTH + + +# Configure rate limiting +limiter = Limiter(key_func=get_remote_address, app=app, default_limits=["5 per minute"]) + + +# Function to check if the file extension is allowed +def allowed_file(filename): + return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS + + +# Function to process the uploaded image +def process_image(file): + # Check if the file is allowed + if not allowed_file(file.filename): + return None + + image = Image.open(BytesIO(file.read())) img_processor = ImageProcessor(device="cpu") return img_processor.process_image(image) +# Function to manage file limit in the upload folder +def manage_file_limit(upload_folder): + files_in_directory = os.listdir(upload_folder) + number_of_files = len(files_in_directory) + + if number_of_files >= MAX_FILES_IN_UPLOAD_FOLDER: + oldest_file = min( + files_in_directory, + key=lambda x: os.path.getctime(os.path.join(upload_folder, x)), + ) + os.remove(os.path.join(upload_folder, oldest_file)) + + def get_inference_class(model_type, model_loader): if model_type == "pytorch": return PyTorchInference(model_loader, device="cpu") @@ -69,6 +110,11 @@ def run_all_benchmarks(img_batch): return benchmark_results +@app.errorhandler(RequestEntityTooLarge) +def handle_file_too_large(e): + return "File is too large", 413 + + @app.route("/demo") def index(): return render_template("demo.html") @@ -81,11 +127,37 @@ def process_request(): mode = request.form.get("mode") # Add logging statements - logging.info("Received request with model_type: %s and mode: %s", model_type, mode) + logging.info( + "Received request with model_type: %s and mode: %s, image_file: %s", + model_type, + mode, + image_file, + ) + + # Manage file limit + manage_file_limit(app.config["UPLOAD_FOLDER"]) - logging.info("Processing image") + # Generate a unique filename using UUID + ext = image_file.filename.rsplit(".", 1)[1].lower() # Get the file extension + unique_filename = f"{uuid.uuid4().hex}.{ext}" + file_path = os.path.join(app.config["UPLOAD_FOLDER"], unique_filename) + + # Save the uploaded file with the unique name + image_file.seek(0) # Reset the file pointer + image_file.save(file_path) + + if image_file is None: + return jsonify({"error": "No file part"}), 400 + + if image_file.filename == "": + return jsonify({"error": "No selected file"}), 400 + + # Process the uploaded image img_batch = process_image(image_file) + if img_batch is None: + return jsonify({"error": "Invalid file type"}), 400 + logging.info("Loading pre-trained model") model_loader = ModelLoader(device="cpu") From 36089ec817fcf381cd8c484354f66bb3aee0ae95 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:18:09 +0200 Subject: [PATCH 030/279] Added file upload support and limits --- src/web_demo/app.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 0091199..e431055 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -134,9 +134,15 @@ def process_request(): image_file, ) + logging.info( + "manage_file_limit:" + ) # Manage file limit manage_file_limit(app.config["UPLOAD_FOLDER"]) + logging.info( + "Generate file name" + ) # Generate a unique filename using UUID ext = image_file.filename.rsplit(".", 1)[1].lower() # Get the file extension unique_filename = f"{uuid.uuid4().hex}.{ext}" @@ -146,6 +152,11 @@ def process_request(): image_file.seek(0) # Reset the file pointer image_file.save(file_path) + logging.info( + "Result file name: %s", + file_path + ) + if image_file is None: return jsonify({"error": "No file part"}), 400 From 73ca3a4d9e81a4f5bd0a3b27e1e76f9584bda921 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:19:42 +0200 Subject: [PATCH 031/279] Added file upload support and limits --- src/web_demo/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index e431055..78a6cfe 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -137,6 +137,9 @@ def process_request(): logging.info( "manage_file_limit:" ) + if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER) + # Manage file limit manage_file_limit(app.config["UPLOAD_FOLDER"]) From fd21b4c09251da50f621a59d924514eefa23e2c8 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:23:15 +0200 Subject: [PATCH 032/279] Use local copy of image --- src/web_demo/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 78a6cfe..f0c41f4 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -167,7 +167,7 @@ def process_request(): return jsonify({"error": "No selected file"}), 400 # Process the uploaded image - img_batch = process_image(image_file) + img_batch = process_image(file_path) if img_batch is None: return jsonify({"error": "Invalid file type"}), 400 From ba9b7303cb6eb07bf7f99f000fb1ed5d4d49af56 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:30:16 +0200 Subject: [PATCH 033/279] Use local copy of image --- src/web_demo/app.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index f0c41f4..5bb361d 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -45,14 +45,11 @@ def allowed_file(filename): # Function to process the uploaded image -def process_image(file): - # Check if the file is allowed - if not allowed_file(file.filename): - return None - - image = Image.open(BytesIO(file.read())) - img_processor = ImageProcessor(device="cpu") - return img_processor.process_image(image) +def process_image(file_path): + # Open the image file + with Image.open(file_path) as image: + img_processor = ImageProcessor(device="cpu") + return img_processor.process_image(image) # Function to manage file limit in the upload folder @@ -166,6 +163,10 @@ def process_request(): if image_file.filename == "": return jsonify({"error": "No selected file"}), 400 + logging.info( + "Process image: %s", + file_path + ) # Process the uploaded image img_batch = process_image(file_path) From 0ebbe3eb1019dee923bbab9b8da249c15e42a4c4 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:35:25 +0200 Subject: [PATCH 034/279] Use local copy of image --- src/web_demo/app.py | 46 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 5bb361d..41bf023 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -124,25 +124,16 @@ def process_request(): mode = request.form.get("mode") # Add logging statements - logging.info( - "Received request with model_type: %s and mode: %s, image_file: %s", - model_type, - mode, - image_file, - ) + logging.info("Received request with model_type: %s, mode: %s, image_file: %s", model_type, mode, image_file.filename) - logging.info( - "manage_file_limit:" - ) - if not os.path.exists(UPLOAD_FOLDER): - os.makedirs(UPLOAD_FOLDER) + if image_file is None or image_file.filename == "": + logging.error("No file part or no selected file") + return jsonify({"error": "No file part or no selected file"}), 400 - # Manage file limit - manage_file_limit(app.config["UPLOAD_FOLDER"]) + if not allowed_file(image_file.filename): + logging.error("Invalid file type: %s", image_file.filename) + return jsonify({"error": "Invalid file type"}), 400 - logging.info( - "Generate file name" - ) # Generate a unique filename using UUID ext = image_file.filename.rsplit(".", 1)[1].lower() # Get the file extension unique_filename = f"{uuid.uuid4().hex}.{ext}" @@ -152,23 +143,12 @@ def process_request(): image_file.seek(0) # Reset the file pointer image_file.save(file_path) - logging.info( - "Result file name: %s", - file_path - ) - - if image_file is None: - return jsonify({"error": "No file part"}), 400 + logging.info("Saved file: %s", file_path) - if image_file.filename == "": - return jsonify({"error": "No selected file"}), 400 - - logging.info( - "Process image: %s", - file_path - ) - # Process the uploaded image - img_batch = process_image(file_path) + # Process the uploaded image using ImageProcessor + device = "cuda" if cuda_is_available() else "cpu" + img_processor = ImageProcessor(img_path=file_path, device=device) + img_batch = img_processor.process_image() if img_batch is None: return jsonify({"error": "Invalid file type"}), 400 @@ -181,11 +161,13 @@ def process_request(): results = run_all_benchmarks(img_batch) return jsonify({"benchmark": results}) + logging.info("Getting inference Class for: %s", mode) inference_class = get_inference_class(model_type, model_loader) if inference_class is None: logging.error("Invalid model type selected: %s", model_type) return jsonify({"error": "Invalid model type selected"}), 400 + logging.info("Running prediction for: %s", mode) if mode == "predict": logging.info("Running prediction") results = inference_class.predict(img_batch) From c95d5d3e5e3ed814cc2d2f6f7c8830d358a37b96 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:37:20 +0200 Subject: [PATCH 035/279] Use local copy of image --- src/web_demo/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 41bf023..a3cd848 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -125,6 +125,8 @@ def process_request(): # Add logging statements logging.info("Received request with model_type: %s, mode: %s, image_file: %s", model_type, mode, image_file.filename) + if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER) if image_file is None or image_file.filename == "": logging.error("No file part or no selected file") From 18a8902c8ac1165c85d3fa26109e18942d82d867 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:45:30 +0200 Subject: [PATCH 036/279] Use local copy of image --- src/web_demo/static/js/scripts.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 80e5335..a54e9b4 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -10,6 +10,7 @@ document.getElementById('image-form').addEventListener('submit', function(e) { }) .then(response => response.json()) .then(data => { + console.log(data) document.getElementById('spinner').style.display = 'none'; if (data.predictions) { displayPredictions(data.predictions); From fa08c2d1351f05375e952da55f5ce57f3161a4c0 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:49:47 +0200 Subject: [PATCH 037/279] Transfer numpy array to the list before jsonoify --- src/web_demo/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index a3cd848..f184a68 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,5 +1,6 @@ import logging +import numpy as np from flask import Flask, render_template, request, jsonify, flash, redirect, url_for from PIL import Image from io import BytesIO @@ -173,6 +174,8 @@ def process_request(): if mode == "predict": logging.info("Running prediction") results = inference_class.predict(img_batch) + if isinstance(results, np.ndarray): + results = results.tolist() return jsonify({"predictions": results}) elif mode == "benchmark": logging.info("Running benchmark") From 0430c81caf9107c8e548c97689338f26230521fa Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:53:18 +0200 Subject: [PATCH 038/279] Transfer numpy array to the list before jsonoify --- src/web_demo/app.py | 14 ++++++++++---- src/web_demo/static/js/scripts.js | 3 ++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index f184a68..8b96ad4 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -125,7 +125,12 @@ def process_request(): mode = request.form.get("mode") # Add logging statements - logging.info("Received request with model_type: %s, mode: %s, image_file: %s", model_type, mode, image_file.filename) + logging.info( + "Received request with model_type: %s, mode: %s, image_file: %s", + model_type, + mode, + image_file.filename, + ) if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) @@ -174,9 +179,10 @@ def process_request(): if mode == "predict": logging.info("Running prediction") results = inference_class.predict(img_batch) - if isinstance(results, np.ndarray): - results = results.tolist() - return jsonify({"predictions": results}) + predictions = [ + {"label": label, "confidence": confidence} for label, confidence in results + ] + return jsonify({"predictions": predictions}) elif mode == "benchmark": logging.info("Running benchmark") results = inference_class.benchmark(img_batch) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index a54e9b4..a76d411 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -29,7 +29,8 @@ function displayPredictions(predictions) { predictions.forEach(prediction => { const p = document.createElement('p'); - p.textContent = `Label: ${prediction.label}, Confidence: ${prediction.confidence.toFixed(2)}`; + const confidence = prediction.confidence ? prediction.confidence.toFixed(2) : 'N/A'; + p.textContent = `Label: ${prediction.label}, Confidence: ${confidence}`; resultsDiv.appendChild(p); }); } From b258b0455e08285cf3e11f918b1dc25b6191f37d Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 00:57:21 +0200 Subject: [PATCH 039/279] Transfer numpy array to the list before jsonoify --- src/web_demo/static/js/scripts.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index a76d411..65ec445 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -11,15 +11,27 @@ document.getElementById('image-form').addEventListener('submit', function(e) { .then(response => response.json()) .then(data => { console.log(data) - document.getElementById('spinner').style.display = 'none'; + document.getElementById('spinner').style.display = 'none'; // Hide spinner + if (data.predictions) { displayPredictions(data.predictions); } else if (data.benchmark) { displayBenchmark(data.benchmark); - } + } else { + // Handle other types of responses or show a message if the response is unexpected + console.log("Unexpected response format:", data); }) .catch(error => { console.error('Error:', error); + document.getElementById('spinner').style.display = 'none'; // Hide spinner + + // Update the UI to show an error message + const resultsDiv = document.getElementById('results'); + resultsDiv.innerHTML = ''; // Clear previous results + const errorMessage = document.createElement('p'); + errorMessage.textContent = 'An error occurred while processing your request.'; + errorMessage.style.color = 'red'; + resultsDiv.appendChild(errorMessage); }); }); From 78fe3a83f145b2b36d612b3ec2f8a31e8223f003 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:03:29 +0200 Subject: [PATCH 040/279] Revert "Transfer numpy array to the list before jsonoify" This reverts commit b258b0455e08285cf3e11f918b1dc25b6191f37d. --- src/web_demo/static/js/scripts.js | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 65ec445..a76d411 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -11,27 +11,15 @@ document.getElementById('image-form').addEventListener('submit', function(e) { .then(response => response.json()) .then(data => { console.log(data) - document.getElementById('spinner').style.display = 'none'; // Hide spinner - + document.getElementById('spinner').style.display = 'none'; if (data.predictions) { displayPredictions(data.predictions); } else if (data.benchmark) { displayBenchmark(data.benchmark); - } else { - // Handle other types of responses or show a message if the response is unexpected - console.log("Unexpected response format:", data); + } }) .catch(error => { console.error('Error:', error); - document.getElementById('spinner').style.display = 'none'; // Hide spinner - - // Update the UI to show an error message - const resultsDiv = document.getElementById('results'); - resultsDiv.innerHTML = ''; // Clear previous results - const errorMessage = document.createElement('p'); - errorMessage.textContent = 'An error occurred while processing your request.'; - errorMessage.style.color = 'red'; - resultsDiv.appendChild(errorMessage); }); }); From 928c18d9ae1c8f54f05722a2f6ec3db0a61dbf76 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:04:31 +0200 Subject: [PATCH 041/279] Revert "Transfer numpy array to the list before jsonoify" This reverts commit 0430c81caf9107c8e548c97689338f26230521fa. --- src/web_demo/app.py | 14 ++++---------- src/web_demo/static/js/scripts.js | 3 +-- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 8b96ad4..f184a68 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -125,12 +125,7 @@ def process_request(): mode = request.form.get("mode") # Add logging statements - logging.info( - "Received request with model_type: %s, mode: %s, image_file: %s", - model_type, - mode, - image_file.filename, - ) + logging.info("Received request with model_type: %s, mode: %s, image_file: %s", model_type, mode, image_file.filename) if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) @@ -179,10 +174,9 @@ def process_request(): if mode == "predict": logging.info("Running prediction") results = inference_class.predict(img_batch) - predictions = [ - {"label": label, "confidence": confidence} for label, confidence in results - ] - return jsonify({"predictions": predictions}) + if isinstance(results, np.ndarray): + results = results.tolist() + return jsonify({"predictions": results}) elif mode == "benchmark": logging.info("Running benchmark") results = inference_class.benchmark(img_batch) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index a76d411..a54e9b4 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -29,8 +29,7 @@ function displayPredictions(predictions) { predictions.forEach(prediction => { const p = document.createElement('p'); - const confidence = prediction.confidence ? prediction.confidence.toFixed(2) : 'N/A'; - p.textContent = `Label: ${prediction.label}, Confidence: ${confidence}`; + p.textContent = `Label: ${prediction.label}, Confidence: ${prediction.confidence.toFixed(2)}`; resultsDiv.appendChild(p); }); } From 86b98d57204624b8957f644d96b7c53f8c5e38d1 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:10:37 +0200 Subject: [PATCH 042/279] Revert "Transfer numpy array to the list before jsonoify" This reverts commit fa08c2d1351f05375e952da55f5ce57f3161a4c0. --- src/web_demo/app.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index f184a68..a3cd848 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,6 +1,5 @@ import logging -import numpy as np from flask import Flask, render_template, request, jsonify, flash, redirect, url_for from PIL import Image from io import BytesIO @@ -174,8 +173,6 @@ def process_request(): if mode == "predict": logging.info("Running prediction") results = inference_class.predict(img_batch) - if isinstance(results, np.ndarray): - results = results.tolist() return jsonify({"predictions": results}) elif mode == "benchmark": logging.info("Running benchmark") From 42c1c2412a20752fb937a60e7fd4e0fab134946a Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:15:31 +0200 Subject: [PATCH 043/279] Transfer numpy array to the list before jsonoify --- src/web_demo/templates/demo.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html index 81fe046..3d80b28 100644 --- a/src/web_demo/templates/demo.html +++ b/src/web_demo/templates/demo.html @@ -7,7 +7,7 @@

ResNetTensorRT Image Processing Demo

-
+
From a89479dc078a3f771cfefd88cf99492aefbef02d Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:19:25 +0200 Subject: [PATCH 044/279] Transfer numpy array to the list before jsonoify --- src/web_demo/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index a3cd848..b291578 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -173,6 +173,7 @@ def process_request(): if mode == "predict": logging.info("Running prediction") results = inference_class.predict(img_batch) + logging.info(f"Prediction results are: {results}") return jsonify({"predictions": results}) elif mode == "benchmark": logging.info("Running benchmark") From cbc706607f18a71272dcebe80d04aec3cc8fb013 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:24:46 +0200 Subject: [PATCH 045/279] Big FIX: probability bug, fixed returning raw probability --- src/inference_base.py | 17 ++++++++++------- src/web_demo/app.py | 6 +++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/inference_base.py b/src/inference_base.py index 45f5c02..8775a54 100644 --- a/src/inference_base.py +++ b/src/inference_base.py @@ -108,20 +108,23 @@ def get_top_predictions(self, prob: np.ndarray, is_benchmark=False): :param prob: Array of probabilities. :param is_benchmark: If True, the method is called during a benchmark run. - :return: Array of probabilities. + :return: List of dictionaries with label and confidence. """ if is_benchmark: return None # Get the top indices and probabilities - top_indices = prob.argsort()[-self.topk :][::-1] + top_indices = prob.argsort()[-self.topk:][::-1] top_probs = prob[top_indices] - # Log and print the top predictions + # Prepare the list of predictions + predictions = [] for i in range(self.topk): probability = top_probs[i] class_label = self.categories[0][int(top_indices[i])] - logging.info(f"#{i + 1}: {int(probability * 100)}% {class_label}") - if self.debug_mode: - print(f"#{i + 1}: {int(probability * 100)}% {class_label}") - return prob + predictions.append({"label": class_label, "confidence": float(probability)}) + + # Log the top predictions + logging.info(f"#{i + 1}: {probability * 100:.2f}% {class_label}") + + return predictions diff --git a/src/web_demo/app.py b/src/web_demo/app.py index b291578..847daca 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -172,9 +172,9 @@ def process_request(): logging.info("Running prediction for: %s", mode) if mode == "predict": logging.info("Running prediction") - results = inference_class.predict(img_batch) - logging.info(f"Prediction results are: {results}") - return jsonify({"predictions": results}) + predictions = inference_class.predict(img_batch) + logging.info(f"Prediction results are: {predictions}") + return jsonify({"predictions": predictions}) elif mode == "benchmark": logging.info("Running benchmark") results = inference_class.benchmark(img_batch) From 2b97cb4d214e1969e7bd5c86fc8960b9d12c082c Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:34:50 +0200 Subject: [PATCH 046/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/app.py | 4 +++- src/web_demo/static/js/scripts.js | 36 +++++++++++++++++++++++++++++++ src/web_demo/templates/demo.html | 21 +++++++++++++++++- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index 847daca..cb87ce5 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -160,6 +160,7 @@ def process_request(): if mode == "benchmark" and model_type == "all": logging.info("Running all benchmarks") + results = run_all_benchmarks(img_batch) return jsonify({"benchmark": results}) @@ -172,11 +173,12 @@ def process_request(): logging.info("Running prediction for: %s", mode) if mode == "predict": logging.info("Running prediction") + predictions = inference_class.predict(img_batch) - logging.info(f"Prediction results are: {predictions}") return jsonify({"predictions": predictions}) elif mode == "benchmark": logging.info("Running benchmark") + results = inference_class.benchmark(img_batch) return jsonify({"benchmark": results}) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index a54e9b4..37b2133 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -4,6 +4,16 @@ document.getElementById('image-form').addEventListener('submit', function(e) { document.getElementById('spinner').style.display = 'block'; + // Display the mini image + let imageInput = document.getElementById('image'); + if (imageInput.files && imageInput.files[0]) { + let reader = new FileReader(); + reader.onload = function(e) { + document.getElementById('processedImage').src = e.target.result; + }; + reader.readAsDataURL(imageInput.files[0]); + } + fetch('/process', { method: 'POST', body: formData @@ -32,6 +42,32 @@ function displayPredictions(predictions) { p.textContent = `Label: ${prediction.label}, Confidence: ${prediction.confidence.toFixed(2)}`; resultsDiv.appendChild(p); }); + + // Prepare data for the graph + const labels = predictions.map(prediction => prediction.label); + const probs = predictions.map(prediction => prediction.confidence * 100); + + const ctx = document.getElementById('probGraph').getContext('2d'); + new Chart(ctx, { + type: 'line', + data: { + labels: labels, + datasets: [{ + label: 'Probability (%)', + data: probs, + backgroundColor: 'rgba(75, 192, 192, 0.2)', + borderColor: 'rgba(75, 192, 192, 1)', + borderWidth: 1 + }] + }, + options: { + scales: { + y: { + beginAtZero: true + } + } + } + }); } diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html index 3d80b28..1561a33 100644 --- a/src/web_demo/templates/demo.html +++ b/src/web_demo/templates/demo.html @@ -7,7 +7,7 @@

ResNetTensorRT Image Processing Demo

- +
@@ -38,7 +38,26 @@

ResNetTensorRT Image Processing Demo

+
+
+ + +
+
+
Processed Image
+ Processed Image +
+
+
Prediction Probabilities
+ +
+
+ + +
+
From 7720d9f6c64f04822600046e70d47b8b2c59915f Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:41:08 +0200 Subject: [PATCH 047/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 35 ++++++++++++++++++++++++------- src/web_demo/templates/base.html | 1 + src/web_demo/templates/demo.html | 4 ++-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 37b2133..195b483 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -35,28 +35,48 @@ document.getElementById('image-form').addEventListener('submit', function(e) { function displayPredictions(predictions) { const resultsDiv = document.getElementById('results'); + const processedImageContainer = document.getElementById('processedImageContainer'); + const probGraphContainer = document.getElementById('probGraphContainer'); + resultsDiv.innerHTML = ''; // Clear previous results + processedImageContainer.style.display = 'block'; + probGraphContainer.style.display = 'block'; + // Display predictions in text format predictions.forEach(prediction => { const p = document.createElement('p'); p.textContent = `Label: ${prediction.label}, Confidence: ${prediction.confidence.toFixed(2)}`; resultsDiv.appendChild(p); }); - // Prepare data for the graph - const labels = predictions.map(prediction => prediction.label); - const probs = predictions.map(prediction => prediction.confidence * 100); + // Display the mini image + let imageInput = document.getElementById('image'); + if (imageInput.files && imageInput.files[0]) { + let reader = new FileReader(); + reader.onload = function(e) { + document.getElementById('processedImage').src = e.target.result; + }; + reader.readAsDataURL(imageInput.files[0]); + } + + // Render prediction probabilities graph + renderProbGraph(predictions); +} +function renderProbGraph(predictions) { const ctx = document.getElementById('probGraph').getContext('2d'); + const labels = predictions.map(prediction => prediction.label); + const probs = predictions.map(prediction => prediction.confidence); + new Chart(ctx, { - type: 'line', + type: 'bar', data: { labels: labels, datasets: [{ - label: 'Probability (%)', + label: 'Confidence', data: probs, - backgroundColor: 'rgba(75, 192, 192, 0.2)', - borderColor: 'rgba(75, 192, 192, 1)', + backgroundColor: 'rgba(54, 162, 235, 0.2)', + borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 1 }] }, @@ -70,7 +90,6 @@ function displayPredictions(predictions) { }); } - function displayBenchmark(benchmarkResults) { const resultsDiv = document.getElementById('results'); resultsDiv.innerHTML = ''; // Clear previous results diff --git a/src/web_demo/templates/base.html b/src/web_demo/templates/base.html index e03f842..6c944d3 100644 --- a/src/web_demo/templates/base.html +++ b/src/web_demo/templates/base.html @@ -3,6 +3,7 @@ {% block title %}{% endblock %} - ResNetTensorRT Demo + {% block head %}{% endblock %} diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html index 1561a33..4a7f8b8 100644 --- a/src/web_demo/templates/demo.html +++ b/src/web_demo/templates/demo.html @@ -43,11 +43,11 @@

ResNetTensorRT Image Processing Demo

-
+ -
+ From 1bbc5c557db02fe44f6bcd011e113fe2586a9be7 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:46:25 +0200 Subject: [PATCH 048/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 195b483..a9f7cc3 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -1,3 +1,5 @@ +let probChart = null; // Global variable to hold the chart instance + document.getElementById('image-form').addEventListener('submit', function(e) { e.preventDefault(); let formData = new FormData(this); @@ -34,21 +36,12 @@ document.getElementById('image-form').addEventListener('submit', function(e) { }); function displayPredictions(predictions) { - const resultsDiv = document.getElementById('results'); const processedImageContainer = document.getElementById('processedImageContainer'); const probGraphContainer = document.getElementById('probGraphContainer'); - resultsDiv.innerHTML = ''; // Clear previous results processedImageContainer.style.display = 'block'; probGraphContainer.style.display = 'block'; - // Display predictions in text format - predictions.forEach(prediction => { - const p = document.createElement('p'); - p.textContent = `Label: ${prediction.label}, Confidence: ${prediction.confidence.toFixed(2)}`; - resultsDiv.appendChild(p); - }); - // Display the mini image let imageInput = document.getElementById('image'); if (imageInput.files && imageInput.files[0]) { @@ -65,24 +58,31 @@ function displayPredictions(predictions) { function renderProbGraph(predictions) { const ctx = document.getElementById('probGraph').getContext('2d'); + + // Destroy the existing chart if it exists + if (probChart) { + probChart.destroy(); + } + const labels = predictions.map(prediction => prediction.label); - const probs = predictions.map(prediction => prediction.confidence); + const probs = predictions.map(prediction => (prediction.confidence * 100).toFixed(2)); // Convert to percentage + const backgroundColors = predictions.map(() => `rgba(${randomRGB()}, ${randomRGB()}, ${randomRGB()}, 0.2)`); // Random colors - new Chart(ctx, { - type: 'bar', + probChart = new Chart(ctx, { + type: 'horizontalBar', data: { labels: labels, datasets: [{ - label: 'Confidence', + label: 'Confidence (%)', data: probs, - backgroundColor: 'rgba(54, 162, 235, 0.2)', - borderColor: 'rgba(54, 162, 235, 1)', + backgroundColor: backgroundColors, + borderColor: backgroundColors.map(color => color.replace('0.2', '1')), // Darker border color borderWidth: 1 }] }, options: { scales: { - y: { + x: { beginAtZero: true } } From b352a9efcd0c7f059c7ade50361c2a3f5af7a5b2 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:47:43 +0200 Subject: [PATCH 049/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index a9f7cc3..8e6465a 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -109,6 +109,10 @@ function displayBenchmark(benchmarkResults) { } } +function randomRGB() { + return Math.floor(Math.random() * 255); +} + function displayLineGraph(data) { document.getElementById('lineGraphContainer').style.display = 'block'; From 561fc7572ccfb5cb2e9278bcfb3113a82de7d2a7 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:50:03 +0200 Subject: [PATCH 050/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 8e6465a..261fd27 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -3,7 +3,10 @@ let probChart = null; // Global variable to hold the chart instance document.getElementById('image-form').addEventListener('submit', function(e) { e.preventDefault(); let formData = new FormData(this); + let submitButton = document.querySelector("#image-form button[type='submit']"); + // Disable the submit button and show the spinner + submitButton.disabled = true; document.getElementById('spinner').style.display = 'block'; // Display the mini image @@ -23,7 +26,10 @@ document.getElementById('image-form').addEventListener('submit', function(e) { .then(response => response.json()) .then(data => { console.log(data) + // Enable the submit button and hide the spinner + submitButton.disabled = false; document.getElementById('spinner').style.display = 'none'; + if (data.predictions) { displayPredictions(data.predictions); } else if (data.benchmark) { @@ -32,6 +38,9 @@ document.getElementById('image-form').addEventListener('submit', function(e) { }) .catch(error => { console.error('Error:', error); + // Enable the submit button in case of an error + submitButton.disabled = false; + document.getElementById('spinner').style.display = 'none'; }); }); From a2de41666b3b59026ce733be24de3c2dba83ccd0 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:54:23 +0200 Subject: [PATCH 051/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 76 +++++++++++++++++-------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 261fd27..3561706 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -45,58 +45,71 @@ document.getElementById('image-form').addEventListener('submit', function(e) { }); function displayPredictions(predictions) { - const processedImageContainer = document.getElementById('processedImageContainer'); - const probGraphContainer = document.getElementById('probGraphContainer'); - - processedImageContainer.style.display = 'block'; - probGraphContainer.style.display = 'block'; + // Clear previous results and hide the processed image and graph container + const resultsDiv = document.getElementById('results'); + const processedImageDiv = document.getElementById('processedImageContainer'); + const probGraphDiv = document.getElementById('probGraphContainer'); + resultsDiv.innerHTML = ''; + processedImageDiv.style.display = 'none'; + probGraphDiv.style.display = 'none'; + + // Check if a chart instance already exists + if (window.probChart) { + window.probChart.destroy(); // Destroy the existing chart + } - // Display the mini image + // Display the processed image let imageInput = document.getElementById('image'); if (imageInput.files && imageInput.files[0]) { let reader = new FileReader(); reader.onload = function(e) { document.getElementById('processedImage').src = e.target.result; + processedImageDiv.style.display = 'block'; }; reader.readAsDataURL(imageInput.files[0]); } - // Render prediction probabilities graph - renderProbGraph(predictions); -} - -function renderProbGraph(predictions) { - const ctx = document.getElementById('probGraph').getContext('2d'); - - // Destroy the existing chart if it exists - if (probChart) { - probChart.destroy(); - } - + // Prepare data for the probability graph const labels = predictions.map(prediction => prediction.label); - const probs = predictions.map(prediction => (prediction.confidence * 100).toFixed(2)); // Convert to percentage - const backgroundColors = predictions.map(() => `rgba(${randomRGB()}, ${randomRGB()}, ${randomRGB()}, 0.2)`); // Random colors + const data = predictions.map(prediction => prediction.confidence * 100); // Convert to percentage - probChart = new Chart(ctx, { - type: 'horizontalBar', + // Create a new chart + const ctx = document.getElementById('probGraph').getContext('2d'); + window.probChart = new Chart(ctx, { + type: 'bar', data: { labels: labels, datasets: [{ label: 'Confidence (%)', - data: probs, - backgroundColor: backgroundColors, - borderColor: backgroundColors.map(color => color.replace('0.2', '1')), // Darker border color - borderWidth: 1 + data: data, + backgroundColor: generateColors(data.length), // Generate different colors for each bar }] }, options: { + indexAxis: 'y', // Horizontal bar chart scales: { - x: { - beginAtZero: true - } + xAxes: [{ + ticks: { + beginAtZero: true + } + }] } } }); + + probGraphDiv.style.display = 'block'; // Display the graph container +} + +// Function to generate random colors +function generateColors(count) { + const colors = []; + for (let i = 0; i < count; i++) { + const r = Math.floor(Math.random() * 255); + const g = Math.floor(Math.random() * 255); + const b = Math.floor(Math.random() * 255); + colors.push(`rgba(${r}, ${g}, ${b}, 0.5)`); + } + return colors; } function displayBenchmark(benchmarkResults) { @@ -118,11 +131,6 @@ function displayBenchmark(benchmarkResults) { } } -function randomRGB() { - return Math.floor(Math.random() * 255); -} - - function displayLineGraph(data) { document.getElementById('lineGraphContainer').style.display = 'block'; From 0542548818438b3f46df7086ca9ff7ea16fe864a Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 01:59:19 +0200 Subject: [PATCH 052/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 53 ++++++++++++++++++------------- src/web_demo/templates/base.html | 1 + 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 3561706..361aa7d 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -45,35 +45,34 @@ document.getElementById('image-form').addEventListener('submit', function(e) { }); function displayPredictions(predictions) { - // Clear previous results and hide the processed image and graph container + // Clear previous results const resultsDiv = document.getElementById('results'); - const processedImageDiv = document.getElementById('processedImageContainer'); - const probGraphDiv = document.getElementById('probGraphContainer'); resultsDiv.innerHTML = ''; - processedImageDiv.style.display = 'none'; - probGraphDiv.style.display = 'none'; - // Check if a chart instance already exists - if (window.probChart) { - window.probChart.destroy(); // Destroy the existing chart - } - - // Display the processed image + // Display the processed image with a smaller size let imageInput = document.getElementById('image'); if (imageInput.files && imageInput.files[0]) { let reader = new FileReader(); reader.onload = function(e) { - document.getElementById('processedImage').src = e.target.result; + let processedImage = document.getElementById('processedImage'); + processedImage.src = e.target.result; + processedImage.style.width = '150px'; // Adjust width as needed + processedImage.style.height = 'auto'; processedImageDiv.style.display = 'block'; }; reader.readAsDataURL(imageInput.files[0]); } // Prepare data for the probability graph - const labels = predictions.map(prediction => prediction.label); - const data = predictions.map(prediction => prediction.confidence * 100); // Convert to percentage + const labels = predictions.map(p => p.label); + const probs = predictions.map(p => p.confidence * 100); // Convert to percentage + + // Destroy the previous chart if it exists + if (window.probChart) { + window.probChart.destroy(); + } - // Create a new chart + // Create a new chart with datalabels for percentages const ctx = document.getElementById('probGraph').getContext('2d'); window.probChart = new Chart(ctx, { type: 'bar', @@ -81,23 +80,33 @@ function displayPredictions(predictions) { labels: labels, datasets: [{ label: 'Confidence (%)', - data: data, - backgroundColor: generateColors(data.length), // Generate different colors for each bar + data: probs, + backgroundColor: 'rgba(54, 162, 235, 0.2)', + borderColor: 'rgba(54, 162, 235, 1)', + borderWidth: 1 }] }, options: { - indexAxis: 'y', // Horizontal bar chart scales: { - xAxes: [{ + yAxes: [{ ticks: { beginAtZero: true } }] + }, + plugins: { + datalabels: { + color: '#000', + anchor: 'end', + align: 'top', + formatter: (value, context) => { + return value.toFixed(2) + '%'; // Format the label with percentage + } + } } - } + }, + plugins: [ChartDataLabels] // Ensure this plugin is included in your project }); - - probGraphDiv.style.display = 'block'; // Display the graph container } // Function to generate random colors diff --git a/src/web_demo/templates/base.html b/src/web_demo/templates/base.html index 6c944d3..c2811de 100644 --- a/src/web_demo/templates/base.html +++ b/src/web_demo/templates/base.html @@ -4,6 +4,7 @@ {% block title %}{% endblock %} - ResNetTensorRT Demo + {% block head %}{% endblock %} From c2d5722dc0e6dec24550326114bcd84ba6420cde Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:05:42 +0200 Subject: [PATCH 053/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 361aa7d..6705256 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -49,7 +49,7 @@ function displayPredictions(predictions) { const resultsDiv = document.getElementById('results'); resultsDiv.innerHTML = ''; - // Display the processed image with a smaller size + // Display the processed image let imageInput = document.getElementById('image'); if (imageInput.files && imageInput.files[0]) { let reader = new FileReader(); @@ -57,7 +57,10 @@ function displayPredictions(predictions) { let processedImage = document.getElementById('processedImage'); processedImage.src = e.target.result; processedImage.style.width = '150px'; // Adjust width as needed - processedImage.style.height = 'auto'; + processedImage.style.height = 'auto'; // Maintain aspect ratio + + // Display the div containing the processed image + let processedImageDiv = document.getElementById('processedImageDiv'); processedImageDiv.style.display = 'block'; }; reader.readAsDataURL(imageInput.files[0]); From edd00f92ddae5638e1dacd860e8be05c794804b7 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:10:15 +0200 Subject: [PATCH 054/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 6705256..e5292c7 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -55,13 +55,14 @@ function displayPredictions(predictions) { let reader = new FileReader(); reader.onload = function(e) { let processedImage = document.getElementById('processedImage'); - processedImage.src = e.target.result; - processedImage.style.width = '150px'; // Adjust width as needed - processedImage.style.height = 'auto'; // Maintain aspect ratio + let processedImageContainer = document.getElementById('processedImageContainer'); - // Display the div containing the processed image - let processedImageDiv = document.getElementById('processedImageDiv'); - processedImageDiv.style.display = 'block'; + if (processedImage && processedImageContainer) { + processedImage.src = e.target.result; + processedImage.style.maxWidth = '150px'; // Adjust width as needed + processedImage.style.height = 'auto'; // Maintain aspect ratio + processedImageContainer.style.display = 'block'; + } }; reader.readAsDataURL(imageInput.files[0]); } From abfe73d0eebe90458825b1fab0cc273a8c060e16 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:17:01 +0200 Subject: [PATCH 055/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index e5292c7..5e86d7d 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -46,7 +46,11 @@ document.getElementById('image-form').addEventListener('submit', function(e) { function displayPredictions(predictions) { // Clear previous results + // Clear previous results and hide the processed image and graph container const resultsDiv = document.getElementById('results'); + const processedImageDiv = document.getElementById('processedImageContainer'); + const probGraphDiv = document.getElementById('probGraphContainer'); + resultsDiv.innerHTML = ''; // Display the processed image From 542ffe4e226feb3531f25abc749971032cbcfc3a Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:21:16 +0200 Subject: [PATCH 056/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 5e86d7d..9d5ae72 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -74,6 +74,7 @@ function displayPredictions(predictions) { // Prepare data for the probability graph const labels = predictions.map(p => p.label); const probs = predictions.map(p => p.confidence * 100); // Convert to percentage + const backgroundColors = generateColors(predictions.length); // Generate random colors for each bar // Destroy the previous chart if it exists if (window.probChart) { @@ -89,18 +90,21 @@ function displayPredictions(predictions) { datasets: [{ label: 'Confidence (%)', data: probs, - backgroundColor: 'rgba(54, 162, 235, 0.2)', - borderColor: 'rgba(54, 162, 235, 1)', + backgroundColor: backgroundColors, + borderColor: backgroundColors.map(color => color.replace('0.5', '1')), // Darker border color borderWidth: 1 }] }, options: { scales: { - yAxes: [{ + y: { // Updated for Chart.js v3 + beginAtZero: true, ticks: { - beginAtZero: true + callback: function(value) { + return value + '%'; // Append '%' to y-axis labels + } } - }] + } }, plugins: { datalabels: { From 760be19a025ab8d02fc78ad6d48ddf991db7a49e Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:24:10 +0200 Subject: [PATCH 057/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 101 +++++++++++++----------------- 1 file changed, 45 insertions(+), 56 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 9d5ae72..7558a92 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -45,92 +45,81 @@ document.getElementById('image-form').addEventListener('submit', function(e) { }); function displayPredictions(predictions) { - // Clear previous results - // Clear previous results and hide the processed image and graph container - const resultsDiv = document.getElementById('results'); - const processedImageDiv = document.getElementById('processedImageContainer'); - const probGraphDiv = document.getElementById('probGraphContainer'); + const processedImageContainer = document.getElementById('processedImageContainer'); + const probGraphContainer = document.getElementById('probGraphContainer'); - resultsDiv.innerHTML = ''; + processedImageContainer.style.display = 'block'; + probGraphContainer.style.display = 'block'; - // Display the processed image + // Display the mini image let imageInput = document.getElementById('image'); if (imageInput.files && imageInput.files[0]) { let reader = new FileReader(); reader.onload = function(e) { - let processedImage = document.getElementById('processedImage'); - let processedImageContainer = document.getElementById('processedImageContainer'); - - if (processedImage && processedImageContainer) { - processedImage.src = e.target.result; - processedImage.style.maxWidth = '150px'; // Adjust width as needed - processedImage.style.height = 'auto'; // Maintain aspect ratio - processedImageContainer.style.display = 'block'; - } + document.getElementById('processedImage').src = e.target.result; }; reader.readAsDataURL(imageInput.files[0]); } - // Prepare data for the probability graph - const labels = predictions.map(p => p.label); - const probs = predictions.map(p => p.confidence * 100); // Convert to percentage - const backgroundColors = generateColors(predictions.length); // Generate random colors for each bar + // Render prediction probabilities graph + renderProbGraph(predictions); +} + +function renderProbGraph(predictions) { + const ctx = document.getElementById('probGraph').getContext('2d'); - // Destroy the previous chart if it exists - if (window.probChart) { - window.probChart.destroy(); + // Destroy the existing chart if it exists + if (probChart) { + probChart.destroy(); } - // Create a new chart with datalabels for percentages - const ctx = document.getElementById('probGraph').getContext('2d'); - window.probChart = new Chart(ctx, { - type: 'bar', + const labels = predictions.map(prediction => prediction.label); + const probs = predictions.map(prediction => (prediction.confidence * 100).toFixed(2)); // Convert to percentage + const backgroundColors = predictions.map(() => `rgba(${randomRGB()}, ${randomRGB()}, ${randomRGB()}, 0.2)`); // Random colors + + probChart = new Chart(ctx, { + type: 'horizontalBar', data: { labels: labels, datasets: [{ label: 'Confidence (%)', data: probs, backgroundColor: backgroundColors, - borderColor: backgroundColors.map(color => color.replace('0.5', '1')), // Darker border color + borderColor: backgroundColors.map(color => color.replace('0.2', '1')), // Darker border color borderWidth: 1 }] }, options: { scales: { - y: { // Updated for Chart.js v3 - beginAtZero: true, - ticks: { - callback: function(value) { - return value + '%'; // Append '%' to y-axis labels - } - } - } - }, - plugins: { - datalabels: { - color: '#000', - anchor: 'end', - align: 'top', - formatter: (value, context) => { - return value.toFixed(2) + '%'; // Format the label with percentage - } + x: { + beginAtZero: true } } - }, - plugins: [ChartDataLabels] // Ensure this plugin is included in your project + } }); } -// Function to generate random colors -function generateColors(count) { - const colors = []; - for (let i = 0; i < count; i++) { - const r = Math.floor(Math.random() * 255); - const g = Math.floor(Math.random() * 255); - const b = Math.floor(Math.random() * 255); - colors.push(`rgba(${r}, ${g}, ${b}, 0.5)`); +function displayBenchmark(benchmarkResults) { + const resultsDiv = document.getElementById('results'); + resultsDiv.innerHTML = ''; // Clear previous results + + for (const model in benchmarkResults) { + const time = benchmarkResults[model].avgTime; + const throughput = benchmarkResults[model].avgThroughput; + + const p = document.createElement('p'); + p.textContent = `${model} - Average Time: ${time.toFixed(2)} ms, Throughput: ${throughput.toFixed(2)}`; + resultsDiv.appendChild(p); } - return colors; + + // If you have data for plotting (e.g., for 'ALL' mode), call displayLineGraph + if (benchmarkResults['all']) { + displayLineGraph(benchmarkResults['all']); + } +} + +function randomRGB() { + return Math.floor(Math.random() * 255); } function displayBenchmark(benchmarkResults) { From 3579f4439b698f821f8535ec6dab421f91e8b828 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:27:14 +0200 Subject: [PATCH 058/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 7558a92..cf280d2 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -78,7 +78,7 @@ function renderProbGraph(predictions) { const backgroundColors = predictions.map(() => `rgba(${randomRGB()}, ${randomRGB()}, ${randomRGB()}, 0.2)`); // Random colors probChart = new Chart(ctx, { - type: 'horizontalBar', + type: 'bAR', data: { labels: labels, datasets: [{ @@ -90,6 +90,7 @@ function renderProbGraph(predictions) { }] }, options: { + indexAxis: 'y', // Set to 'y' for horizontal bars scales: { x: { beginAtZero: true From 2e6bb1c29b7cf5a927f136cf589b2845d29571b5 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:28:33 +0200 Subject: [PATCH 059/279] Refactored HTML added Image mini picture and prediction results as a line graph --- src/web_demo/static/js/scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index cf280d2..215ec08 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -78,7 +78,7 @@ function renderProbGraph(predictions) { const backgroundColors = predictions.map(() => `rgba(${randomRGB()}, ${randomRGB()}, ${randomRGB()}, 0.2)`); // Random colors probChart = new Chart(ctx, { - type: 'bAR', + type: 'bar', data: { labels: labels, datasets: [{ From d06c60858c7a6c6b4f37ee97e5add363ce702454 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:32:30 +0200 Subject: [PATCH 060/279] Trying to apply image resize --- src/web_demo/static/js/scripts.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 215ec08..fcc71f8 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -56,7 +56,12 @@ function displayPredictions(predictions) { if (imageInput.files && imageInput.files[0]) { let reader = new FileReader(); reader.onload = function(e) { - document.getElementById('processedImage').src = e.target.result; + let processedImage = document.getElementById('processedImage'); + if (processedImage) { + processedImage.src = e.target.result; + processedImage.style.maxWidth = '150px'; // Adjust width as needed + processedImage.style.height = 'auto'; // Maintain aspect ratio + } }; reader.readAsDataURL(imageInput.files[0]); } From 5edbcf31b349a49da88579e8e76588f6916c7ef7 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:34:00 +0200 Subject: [PATCH 061/279] Trying to apply image resize --- src/web_demo/static/js/scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index fcc71f8..13849ff 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -59,7 +59,7 @@ function displayPredictions(predictions) { let processedImage = document.getElementById('processedImage'); if (processedImage) { processedImage.src = e.target.result; - processedImage.style.maxWidth = '150px'; // Adjust width as needed + processedImage.style.maxWidth = '300px'; // Adjust width as needed processedImage.style.height = 'auto'; // Maintain aspect ratio } }; @@ -90,7 +90,7 @@ function renderProbGraph(predictions) { label: 'Confidence (%)', data: probs, backgroundColor: backgroundColors, - borderColor: backgroundColors.map(color => color.replace('0.2', '1')), // Darker border color + borderColor: backgroundColors.map(color => color.replace('0.45', '1')), // Darker border color borderWidth: 1 }] }, From fd37f175bc8d07b05ad5afab169981d2f7e10de1 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:35:23 +0200 Subject: [PATCH 062/279] Trying to apply image resize --- src/web_demo/static/js/scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 13849ff..9794774 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -59,7 +59,7 @@ function displayPredictions(predictions) { let processedImage = document.getElementById('processedImage'); if (processedImage) { processedImage.src = e.target.result; - processedImage.style.maxWidth = '300px'; // Adjust width as needed + processedImage.style.maxWidth = '450px'; // Adjust width as needed processedImage.style.height = 'auto'; // Maintain aspect ratio } }; @@ -90,7 +90,7 @@ function renderProbGraph(predictions) { label: 'Confidence (%)', data: probs, backgroundColor: backgroundColors, - borderColor: backgroundColors.map(color => color.replace('0.45', '1')), // Darker border color + borderColor: backgroundColors.map(color => color.replace('0.15', '1')), // Darker border color borderWidth: 1 }] }, From fb1e864bf0c35ee96988605eb5836ec6dcf3d998 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 02:37:15 +0200 Subject: [PATCH 063/279] Less transparent bars --- src/web_demo/static/js/scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 9794774..28eece9 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -80,7 +80,7 @@ function renderProbGraph(predictions) { const labels = predictions.map(prediction => prediction.label); const probs = predictions.map(prediction => (prediction.confidence * 100).toFixed(2)); // Convert to percentage - const backgroundColors = predictions.map(() => `rgba(${randomRGB()}, ${randomRGB()}, ${randomRGB()}, 0.2)`); // Random colors + const backgroundColors = predictions.map(() => `rgba(${randomRGB()}, ${randomRGB()}, ${randomRGB()}, 0.45)`); // Random colors probChart = new Chart(ctx, { type: 'bar', @@ -90,7 +90,7 @@ function renderProbGraph(predictions) { label: 'Confidence (%)', data: probs, backgroundColor: backgroundColors, - borderColor: backgroundColors.map(color => color.replace('0.15', '1')), // Darker border color + borderColor: backgroundColors.map(color => color.replace('0.2', '1')), // Darker border color borderWidth: 1 }] }, From 78e96dbc22710f203ed107642ff5ea8cdb79d357 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 10:51:59 +0200 Subject: [PATCH 064/279] Redesign --- src/web_demo/static/js/scripts.js | 19 ------------------- src/web_demo/templates/demo.html | 26 +++++++++++++++++++------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 28eece9..c7ba4ec 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -105,25 +105,6 @@ function renderProbGraph(predictions) { }); } -function displayBenchmark(benchmarkResults) { - const resultsDiv = document.getElementById('results'); - resultsDiv.innerHTML = ''; // Clear previous results - - for (const model in benchmarkResults) { - const time = benchmarkResults[model].avgTime; - const throughput = benchmarkResults[model].avgThroughput; - - const p = document.createElement('p'); - p.textContent = `${model} - Average Time: ${time.toFixed(2)} ms, Throughput: ${throughput.toFixed(2)}`; - resultsDiv.appendChild(p); - } - - // If you have data for plotting (e.g., for 'ALL' mode), call displayLineGraph - if (benchmarkResults['all']) { - displayLineGraph(benchmarkResults['all']); - } -} - function randomRGB() { return Math.floor(Math.random() * 255); } diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html index 4a7f8b8..c7cb8b7 100644 --- a/src/web_demo/templates/demo.html +++ b/src/web_demo/templates/demo.html @@ -5,7 +5,7 @@

ResNetTensorRT Image Processing Demo

-
+
@@ -44,12 +44,20 @@

ResNetTensorRT Image Processing Demo

@@ -57,8 +65,12 @@
Prediction Probabilities
From 2f4d04a11f288dff495fd4890b0a55810589c575 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 10:57:43 +0200 Subject: [PATCH 065/279] Redesign and adding inference time --- src/web_demo/app.py | 7 +++++-- src/web_demo/static/js/scripts.js | 8 +++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index cb87ce5..f1adb64 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,3 +1,4 @@ +import time import logging from flask import Flask, render_template, request, jsonify, flash, redirect, url_for @@ -173,9 +174,11 @@ def process_request(): logging.info("Running prediction for: %s", mode) if mode == "predict": logging.info("Running prediction") - + start_time = time.time() predictions = inference_class.predict(img_batch) - return jsonify({"predictions": predictions}) + end_time = time.time() + inference_time = (end_time - start_time) * 1000 + return jsonify({"predictions": predictions, "inferenceTime": inference_time}) elif mode == "benchmark": logging.info("Running benchmark") diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index c7ba4ec..4d6b671 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -26,12 +26,13 @@ document.getElementById('image-form').addEventListener('submit', function(e) { .then(response => response.json()) .then(data => { console.log(data) + // Enable the submit button and hide the spinner submitButton.disabled = false; document.getElementById('spinner').style.display = 'none'; if (data.predictions) { - displayPredictions(data.predictions); + displayPredictions(data.predictions, data.inferenceTime); } else if (data.benchmark) { displayBenchmark(data.benchmark); } @@ -51,6 +52,11 @@ function displayPredictions(predictions) { processedImageContainer.style.display = 'block'; probGraphContainer.style.display = 'block'; + // Display inference time + const timeDiv = document.createElement('div'); + timeDiv.textContent = `Inference time: ${inferenceTime.toFixed(3)} ms`; + document.getElementById('results').appendChild(timeDiv); + // Display the mini image let imageInput = document.getElementById('image'); if (imageInput.files && imageInput.files[0]) { From 440f3943f0da32ea59164671da2b46e03711964e Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 10:59:54 +0200 Subject: [PATCH 066/279] Redesign and adding inference time --- src/web_demo/app.py | 7 +++++-- src/web_demo/static/js/scripts.js | 8 +++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index cb87ce5..f1adb64 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,3 +1,4 @@ +import time import logging from flask import Flask, render_template, request, jsonify, flash, redirect, url_for @@ -173,9 +174,11 @@ def process_request(): logging.info("Running prediction for: %s", mode) if mode == "predict": logging.info("Running prediction") - + start_time = time.time() predictions = inference_class.predict(img_batch) - return jsonify({"predictions": predictions}) + end_time = time.time() + inference_time = (end_time - start_time) * 1000 + return jsonify({"predictions": predictions, "inferenceTime": inference_time}) elif mode == "benchmark": logging.info("Running benchmark") diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index c7ba4ec..4d6b671 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -26,12 +26,13 @@ document.getElementById('image-form').addEventListener('submit', function(e) { .then(response => response.json()) .then(data => { console.log(data) + // Enable the submit button and hide the spinner submitButton.disabled = false; document.getElementById('spinner').style.display = 'none'; if (data.predictions) { - displayPredictions(data.predictions); + displayPredictions(data.predictions, data.inferenceTime); } else if (data.benchmark) { displayBenchmark(data.benchmark); } @@ -51,6 +52,11 @@ function displayPredictions(predictions) { processedImageContainer.style.display = 'block'; probGraphContainer.style.display = 'block'; + // Display inference time + const timeDiv = document.createElement('div'); + timeDiv.textContent = `Inference time: ${inferenceTime.toFixed(3)} ms`; + document.getElementById('results').appendChild(timeDiv); + // Display the mini image let imageInput = document.getElementById('image'); if (imageInput.files && imageInput.files[0]) { From e02418b30fe3d34ca453ee7a36b3666a6003d5bc Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 11:01:40 +0200 Subject: [PATCH 067/279] Revert "Redesign and adding inference time" This reverts commit 440f3943f0da32ea59164671da2b46e03711964e. --- src/web_demo/app.py | 7 ++----- src/web_demo/static/js/scripts.js | 8 +------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index f1adb64..cb87ce5 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,4 +1,3 @@ -import time import logging from flask import Flask, render_template, request, jsonify, flash, redirect, url_for @@ -174,11 +173,9 @@ def process_request(): logging.info("Running prediction for: %s", mode) if mode == "predict": logging.info("Running prediction") - start_time = time.time() + predictions = inference_class.predict(img_batch) - end_time = time.time() - inference_time = (end_time - start_time) * 1000 - return jsonify({"predictions": predictions, "inferenceTime": inference_time}) + return jsonify({"predictions": predictions}) elif mode == "benchmark": logging.info("Running benchmark") diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 4d6b671..c7ba4ec 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -26,13 +26,12 @@ document.getElementById('image-form').addEventListener('submit', function(e) { .then(response => response.json()) .then(data => { console.log(data) - // Enable the submit button and hide the spinner submitButton.disabled = false; document.getElementById('spinner').style.display = 'none'; if (data.predictions) { - displayPredictions(data.predictions, data.inferenceTime); + displayPredictions(data.predictions); } else if (data.benchmark) { displayBenchmark(data.benchmark); } @@ -52,11 +51,6 @@ function displayPredictions(predictions) { processedImageContainer.style.display = 'block'; probGraphContainer.style.display = 'block'; - // Display inference time - const timeDiv = document.createElement('div'); - timeDiv.textContent = `Inference time: ${inferenceTime.toFixed(3)} ms`; - document.getElementById('results').appendChild(timeDiv); - // Display the mini image let imageInput = document.getElementById('image'); if (imageInput.files && imageInput.files[0]) { From 45527436b867ceeb5ebb8619a4c4bdcf33f7672c Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 11:35:57 +0200 Subject: [PATCH 068/279] Redesign and adding inference time --- src/web_demo/app.py | 7 ++++++- src/web_demo/static/js/scripts.js | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/web_demo/app.py b/src/web_demo/app.py index cb87ce5..9c2d429 100644 --- a/src/web_demo/app.py +++ b/src/web_demo/app.py @@ -1,3 +1,4 @@ +import time import logging from flask import Flask, render_template, request, jsonify, flash, redirect, url_for @@ -174,8 +175,12 @@ def process_request(): if mode == "predict": logging.info("Running prediction") + start_time = time.time() predictions = inference_class.predict(img_batch) - return jsonify({"predictions": predictions}) + end_time = time.time() + inference_time = (end_time - start_time) * 1000 + + return jsonify({"predictions": predictions, "inference_time": inference_time}) elif mode == "benchmark": logging.info("Running benchmark") diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index c7ba4ec..dee319d 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -31,7 +31,7 @@ document.getElementById('image-form').addEventListener('submit', function(e) { document.getElementById('spinner').style.display = 'none'; if (data.predictions) { - displayPredictions(data.predictions); + displayPredictions(data.predictions, data.inference_time); } else if (data.benchmark) { displayBenchmark(data.benchmark); } @@ -68,6 +68,11 @@ function displayPredictions(predictions) { // Render prediction probabilities graph renderProbGraph(predictions); + + // Display inference time + if (inferenceTimeDiv) { + inferenceTimeDiv.innerHTML = `Inference Time: ${inferenceTime.toFixed(2)} ms`; + } } function renderProbGraph(predictions) { From acdd5ee661fe29b7b4c6ca03087fdbad8b176d0e Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 11:39:32 +0200 Subject: [PATCH 069/279] Redesign and adding inference time --- src/web_demo/static/js/scripts.js | 3 ++- src/web_demo/templates/demo.html | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index dee319d..05323d4 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -70,6 +70,7 @@ function displayPredictions(predictions) { renderProbGraph(predictions); // Display inference time + const inferenceTimeDiv = document.getElementById('inferenceTime'); if (inferenceTimeDiv) { inferenceTimeDiv.innerHTML = `Inference Time: ${inferenceTime.toFixed(2)} ms`; } @@ -85,7 +86,7 @@ function renderProbGraph(predictions) { const labels = predictions.map(prediction => prediction.label); const probs = predictions.map(prediction => (prediction.confidence * 100).toFixed(2)); // Convert to percentage - const backgroundColors = predictions.map(() => `rgba(${randomRGB()}, ${randomRGB()}, ${randomRGB()}, 0.45)`); // Random colors + const backgroundColors = predictions.map(() => `rgba(${randomRGB()}, ${randomRGB()}, ${randomRGB()}, 0.8)`); probChart = new Chart(ctx, { type: 'bar', diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html index c7cb8b7..395ed67 100644 --- a/src/web_demo/templates/demo.html +++ b/src/web_demo/templates/demo.html @@ -57,6 +57,8 @@
Processed Image
Prediction Probabilities
+ +
From 669b4c30dffd365cb1c47fb73711066f28d34bd1 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 11:45:33 +0200 Subject: [PATCH 070/279] Fix displayPredictions --- src/web_demo/static/js/scripts.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 05323d4..4e7aed1 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -44,7 +44,7 @@ document.getElementById('image-form').addEventListener('submit', function(e) { }); }); -function displayPredictions(predictions) { +function displayPredictions(predictions, inferenceTime) { const processedImageContainer = document.getElementById('processedImageContainer'); const probGraphContainer = document.getElementById('probGraphContainer'); @@ -72,7 +72,11 @@ function displayPredictions(predictions) { // Display inference time const inferenceTimeDiv = document.getElementById('inferenceTime'); if (inferenceTimeDiv) { - inferenceTimeDiv.innerHTML = `Inference Time: ${inferenceTime.toFixed(2)} ms`; + if (typeof inferenceTime === 'number') { + inferenceTimeDiv.innerHTML = `Inference Time: ${inferenceTime.toFixed(2)} ms`; + } else { + inferenceTimeDiv.innerHTML = 'Inference Time: N/A'; + } } } From 9b1980f023bb742556a14f6bb8a2ff1dbb8ed830 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 11:52:23 +0200 Subject: [PATCH 071/279] Added styling --- src/web_demo/static/css/styles.css | 15 +++++++++++++++ src/web_demo/templates/demo.html | 9 +++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 src/web_demo/static/css/styles.css diff --git a/src/web_demo/static/css/styles.css b/src/web_demo/static/css/styles.css new file mode 100644 index 0000000..2a413eb --- /dev/null +++ b/src/web_demo/static/css/styles.css @@ -0,0 +1,15 @@ +.inference-time-container { + background-color: #ADD8E6; /* Light blue background */ + border-radius: 20px; /* Makes it oval-shaped */ + padding: 10px 20px; /* Adds some padding inside the container */ + text-align: center; /* Centers the text */ + margin: 0 auto; /* Centers the container horizontally */ + width: fit-content; /* Adjusts width to fit the content */ + animation: pop 0.5s ease; /* Adds a popping animation */ +} + +@keyframes pop { + 0% { transform: scale(0.8); } + 50% { transform: scale(1.2); } + 100% { transform: scale(1); } +} diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html index 395ed67..33a35ab 100644 --- a/src/web_demo/templates/demo.html +++ b/src/web_demo/templates/demo.html @@ -1,5 +1,10 @@ {% extends "base.html" %} +{% block head %} + + +{% endblock %} + {% block content %}
@@ -57,12 +62,12 @@
Processed Image
Prediction Probabilities
- -
+
+
From 84117836d6f7890007d0dfe1a52c66e6fdb671f2 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 11:57:12 +0200 Subject: [PATCH 072/279] Added styling --- src/web_demo/static/css/styles.css | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/web_demo/static/css/styles.css b/src/web_demo/static/css/styles.css index 2a413eb..73aff57 100644 --- a/src/web_demo/static/css/styles.css +++ b/src/web_demo/static/css/styles.css @@ -1,15 +1,20 @@ .inference-time-container { - background-color: #ADD8E6; /* Light blue background */ - border-radius: 20px; /* Makes it oval-shaped */ - padding: 10px 20px; /* Adds some padding inside the container */ - text-align: center; /* Centers the text */ - margin: 0 auto; /* Centers the container horizontally */ - width: fit-content; /* Adjusts width to fit the content */ - animation: pop 0.5s ease; /* Adds a popping animation */ + background-color: #007bff; /* Bootstrap primary color */ + color: white; + text-align: center; + padding: 10px; + border-radius: 20px; + margin-top: 15px; + animation: popIn 0.5s ease; } -@keyframes pop { - 0% { transform: scale(0.8); } - 50% { transform: scale(1.2); } - 100% { transform: scale(1); } -} +@keyframes popIn { + 0% { + transform: scale(0); + opacity: 0; + } + 100% { + transform: scale(1); + opacity: 1; + } +} \ No newline at end of file From ee9509f3d9f62870c4e9c545af5026a94e78a523 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 11:59:47 +0200 Subject: [PATCH 073/279] Added styling --- src/web_demo/templates/demo.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html index 33a35ab..9453136 100644 --- a/src/web_demo/templates/demo.html +++ b/src/web_demo/templates/demo.html @@ -62,8 +62,8 @@
Processed Image
Prediction Probabilities
-
+
From fad4bd81e35cd7dafd006e1544b3677c066e1daa Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 12:03:31 +0200 Subject: [PATCH 074/279] Added styling --- src/web_demo/static/js/scripts.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/web_demo/static/js/scripts.js b/src/web_demo/static/js/scripts.js index 4e7aed1..bb0fc71 100644 --- a/src/web_demo/static/js/scripts.js +++ b/src/web_demo/static/js/scripts.js @@ -69,14 +69,21 @@ function displayPredictions(predictions, inferenceTime) { // Render prediction probabilities graph renderProbGraph(predictions); - // Display inference time - const inferenceTimeDiv = document.getElementById('inferenceTime'); + // Update the inference time container + let inferenceTimeDiv = document.getElementById('inferenceTime'); if (inferenceTimeDiv) { - if (typeof inferenceTime === 'number') { - inferenceTimeDiv.innerHTML = `Inference Time: ${inferenceTime.toFixed(2)} ms`; - } else { - inferenceTimeDiv.innerHTML = 'Inference Time: N/A'; - } + // Remove the element + inferenceTimeDiv.remove(); + + // Create a new div element for inference time + let newInferenceTimeDiv = document.createElement('div'); + newInferenceTimeDiv.id = 'inferenceTime'; + newInferenceTimeDiv.className = 'inference-time-container'; + newInferenceTimeDiv.innerHTML = `Inference Time: ${inferenceTime.toFixed(2)} ms`; + + // Re-add the element to the DOM + let probGraphContainer = document.getElementById('probGraphContainer'); + probGraphContainer.appendChild(newInferenceTimeDiv); } } From d2c8f1dee22a02408bbbec9ebd11dfb42be467da Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 12:19:18 +0200 Subject: [PATCH 075/279] Horizontal page layout --- src/web_demo/templates/demo.html | 57 +++++++++++++------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html index 9453136..5a8c21a 100644 --- a/src/web_demo/templates/demo.html +++ b/src/web_demo/templates/demo.html @@ -12,63 +12,52 @@

ResNetTensorRT Image Processing Demo

- -
- - -
-
- - -
-
- - -
- - +
- +
- From b2ff85b99dcdaa60ab6a65435aadd4c6e2466c4f Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 21:58:04 +0200 Subject: [PATCH 094/279] Color update --- src/web_demo/static/css/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/static/css/styles.css b/src/web_demo/static/css/styles.css index e9caaf1..89e3f29 100644 --- a/src/web_demo/static/css/styles.css +++ b/src/web_demo/static/css/styles.css @@ -16,7 +16,7 @@ border-radius: 20px; margin-top: 7px; animation: popIn 0.5s; - animation-delay: 0.5s; /* Delay the animation */ + animation-delay: 0.25s; /* Delay the animation */ } @keyframes popIn { From b6f902ac017b8c643983d9cc2af0ee9c8b1f9906 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 22:01:35 +0200 Subject: [PATCH 095/279] Color update --- src/web_demo/static/css/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web_demo/static/css/styles.css b/src/web_demo/static/css/styles.css index 89e3f29..3b29fbe 100644 --- a/src/web_demo/static/css/styles.css +++ b/src/web_demo/static/css/styles.css @@ -9,7 +9,7 @@ } .top-prediction-container { - background-color: #FFd166; + background-color: #48bf91; color: white; text-align: center; padding: 10px; From a640a8b11841feed5ae725cb7219a444a397bd72 Mon Sep 17 00:00:00 2001 From: Dima Birenbaum Date: Sat, 23 Dec 2023 22:07:24 +0200 Subject: [PATCH 096/279] Try vertical layout --- src/web_demo/templates/demo.html | 58 ++++++-------------------------- 1 file changed, 10 insertions(+), 48 deletions(-) diff --git a/src/web_demo/templates/demo.html b/src/web_demo/templates/demo.html index 141af49..83e9868 100644 --- a/src/web_demo/templates/demo.html +++ b/src/web_demo/templates/demo.html @@ -23,72 +23,34 @@

ResNetTensorRT Image Processing Demo

-
- - -
-
- - -
-
- - -
- +
- +
- - +
- - +
-