|
|
@@ -45,10 +45,14 @@ html = """<!DOCTYPE html>
|
|
|
<meta charset="UTF-8">
|
|
|
<title>Cell Growth Classifier</title>
|
|
|
<script>
|
|
|
-function uploadImage() {
|
|
|
+function uploadImages() {
|
|
|
var formData = new FormData();
|
|
|
- var imageFile = document.getElementById("imageInput").files[0];
|
|
|
- formData.append("image", imageFile);
|
|
|
+ var imageFiles = document.getElementById("imageInput").files;
|
|
|
+ var imageFilesArray = [];
|
|
|
+ for (var i = 0; i < imageFiles.length; i++) {
|
|
|
+ formData.append("images", imageFiles[i]);
|
|
|
+ imageFilesArray.push({name: imageFiles[i].name, file: imageFiles[i]});
|
|
|
+ }
|
|
|
|
|
|
fetch('/upload', {
|
|
|
method: 'POST',
|
|
|
@@ -56,36 +60,94 @@ function uploadImage() {
|
|
|
})
|
|
|
.then(response => response.json())
|
|
|
.then(data => {
|
|
|
- document.getElementById("results").textContent = `${data.results}`;
|
|
|
- var image = document.createElement("img");
|
|
|
- image.src = URL.createObjectURL(imageFile);
|
|
|
- image.onload = function() {
|
|
|
- URL.revokeObjectURL(image.src) // Free up memory
|
|
|
- }
|
|
|
- document.getElementById("imageDisplay").innerHTML = '';
|
|
|
- document.getElementById("imageDisplay").appendChild(image);
|
|
|
+ var results = data.results;
|
|
|
+ var sortedKeys = Object.keys(results).sort(); // Sort the filenames
|
|
|
+ var table = document.getElementById("resultsTable");
|
|
|
+ table.innerHTML = ""; // Clear the table first
|
|
|
+
|
|
|
+ // Create table header
|
|
|
+ var header = table.createTHead();
|
|
|
+ var headerRow = header.insertRow(0);
|
|
|
+ headerRow.insertCell(0).textContent = "Image";
|
|
|
+ headerRow.insertCell(1).textContent = "Filename";
|
|
|
+ headerRow.insertCell(2).textContent = "Analysis";
|
|
|
+
|
|
|
+ // Create table body
|
|
|
+ var tbody = table.createTBody();
|
|
|
+
|
|
|
+ // Insert a row in the table for each sorted image result
|
|
|
+ sortedKeys.forEach((imageName) => {
|
|
|
+ var imageFile = imageFilesArray.find(file => file.name === imageName);
|
|
|
+ var row = tbody.insertRow(-1);
|
|
|
+ var cellImage = row.insertCell(0);
|
|
|
+ var image = document.createElement("img");
|
|
|
+
|
|
|
+ if (imageFile) {
|
|
|
+ image.src = URL.createObjectURL(imageFile.file);
|
|
|
+ image.onload = function() {
|
|
|
+ URL.revokeObjectURL(this.src) // Free up memory
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ image.alt = "Image not found";
|
|
|
+ }
|
|
|
+ image.style.width = '100px'; // Set the image size
|
|
|
+ cellImage.appendChild(image);
|
|
|
+
|
|
|
+ row.insertCell(1).textContent = imageName;
|
|
|
+ row.insertCell(2).textContent = results[imageName].text;
|
|
|
+ });
|
|
|
+
|
|
|
+ // Set the model used
|
|
|
+ document.getElementById("modelUsed").textContent = "Model used: " + data.model;
|
|
|
})
|
|
|
.catch(error => {
|
|
|
console.error('Error:', error);
|
|
|
- document.getElementById("results").textContent = "An error occurred while uploading the image.";
|
|
|
+ document.getElementById("results").textContent = "An error occurred while uploading the images.";
|
|
|
});
|
|
|
}
|
|
|
</script>
|
|
|
+<style>
|
|
|
+ table {
|
|
|
+ border-collapse: collapse;
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+ thead, td {
|
|
|
+ border: 1px solid #ddd;
|
|
|
+ padding: 8px;
|
|
|
+ text-align: left;
|
|
|
+ }
|
|
|
+ thead {
|
|
|
+ background-color: #f2f2f2;
|
|
|
+ color: #333;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ td {
|
|
|
+ font-size: 0.9em;
|
|
|
+ }
|
|
|
+ img {
|
|
|
+ width: 100px; /* Adjust the size of the image if necessary */
|
|
|
+ height: auto;
|
|
|
+ }
|
|
|
+ #modelUsed {
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+</style>
|
|
|
+
|
|
|
</head>
|
|
|
<body>
|
|
|
|
|
|
-<h2>Cell growth classifier demo</h2>
|
|
|
-<input type="file" id="imageInput" accept="image/*"> <br><br>
|
|
|
-<button onclick="uploadImage()">Upload</button>
|
|
|
-
|
|
|
-<h3>Image:</h3>
|
|
|
-<div id="imageDisplay"></div>
|
|
|
+<h2>Cell Growth Classifier</h2>
|
|
|
+<input type="file" id="imageInput" accept="image/*" multiple>
|
|
|
+<button onclick="uploadImages()">Analyze</button>
|
|
|
|
|
|
-<h3>Results:</h3>
|
|
|
-<div id="results"></div>
|
|
|
+<p id="modelUsed"></p>
|
|
|
+<table id="resultsTable">
|
|
|
+
|
|
|
+</table>
|
|
|
|
|
|
</body>
|
|
|
</html>
|
|
|
+
|
|
|
"""
|
|
|
|
|
|
# ----
|
|
|
@@ -107,19 +169,19 @@ def startServer(index_html:str, predictor:Predictor, host_port:str):
|
|
|
|
|
|
@app.route('/upload', method='POST')
|
|
|
def upload_image():
|
|
|
- upload = request.files.get('image')
|
|
|
- if upload is None:
|
|
|
- return "No image uploaded"
|
|
|
-
|
|
|
- # Read the image file in bytes and open it with Pillow
|
|
|
- image_bytes = io.BytesIO(upload.file.read())
|
|
|
- try:
|
|
|
- with Image.open(image_bytes) as img:
|
|
|
- pred:Prediction = app.predictor.predict(img)
|
|
|
- return {"results": str(pred)}
|
|
|
- except IOError:
|
|
|
- response.status = 400
|
|
|
- return "Invalid image file"
|
|
|
+ uploads = request.files.getall('images')
|
|
|
+ results = {}
|
|
|
+ for upload in uploads:
|
|
|
+ # Read the image file in bytes and open it with Pillow
|
|
|
+ image_bytes = io.BytesIO(upload.file.read())
|
|
|
+ try:
|
|
|
+ with Image.open(image_bytes) as img:
|
|
|
+ pred:Prediction = app.predictor.predict(img)
|
|
|
+ results[upload.filename] = pred.getDict()
|
|
|
+ except IOError:
|
|
|
+ response.status = 400
|
|
|
+ return "Invalid image file"
|
|
|
+ return {"model": app.predictor.modelName, "results": results}
|
|
|
|
|
|
# Store predictor in the bottle app object
|
|
|
app.predictor = predictor
|