|
<!DOCTYPE html> |
|
<html lang="fr"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Labélisation d'Images avec SAM</title> |
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}"> |
|
<style> |
|
body { |
|
font-family: Arial, sans-serif; |
|
margin: 20px; |
|
background-color: #f9f9f9; |
|
} |
|
|
|
h1 { |
|
text-align: center; |
|
color: #333; |
|
} |
|
|
|
section { |
|
margin: 20px 0; |
|
padding: 20px; |
|
background: #fff; |
|
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); |
|
border-radius: 8px; |
|
} |
|
|
|
canvas { |
|
border: 2px solid #ddd; |
|
border-radius: 8px; |
|
display: block; |
|
margin: 20px auto; |
|
} |
|
|
|
button { |
|
background-color: #4CAF50; |
|
color: white; |
|
border: none; |
|
padding: 10px 20px; |
|
font-size: 14px; |
|
cursor: pointer; |
|
border-radius: 5px; |
|
transition: background-color 0.3s ease; |
|
} |
|
|
|
button:hover { |
|
background-color: #45a049; |
|
} |
|
|
|
button:disabled { |
|
background-color: #ccc; |
|
cursor: not-allowed; |
|
} |
|
|
|
.upload-section, .class-management, .controls { |
|
text-align: center; |
|
} |
|
|
|
.class-list { |
|
display: flex; |
|
flex-wrap: wrap; |
|
gap: 10px; |
|
justify-content: center; |
|
list-style: none; |
|
padding: 0; |
|
} |
|
|
|
.class-item { |
|
padding: 5px 10px; |
|
border-radius: 5px; |
|
background-color: #f4f4f4; |
|
border: 1px solid #ccc; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.class-item:hover { |
|
background-color: #ddd; |
|
} |
|
|
|
.class-item.active { |
|
background-color: #4CAF50; |
|
color: white; |
|
border-color: #45a049; |
|
} |
|
|
|
#annotated-image { |
|
display: block; |
|
max-width: 100%; |
|
margin: 20px auto; |
|
border: 2px solid #4CAF50; |
|
border-radius: 8px; |
|
} |
|
|
|
.status { |
|
text-align: center; |
|
margin-top: 10px; |
|
font-weight: bold; |
|
} |
|
|
|
.status.success { |
|
color: #4CAF50; |
|
} |
|
|
|
.status.error { |
|
color: #f44336; |
|
} |
|
|
|
.image-container { |
|
display: flex; |
|
justify-content: center; |
|
flex-wrap: wrap; |
|
gap: 10px; |
|
margin: 20px 0; |
|
} |
|
|
|
.image-item { |
|
width: 150px; |
|
height: 150px; |
|
object-fit: cover; |
|
border: 2px solid #ddd; |
|
border-radius: 8px; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
|
|
.image-item img { |
|
max-width: 100%; |
|
max-height: 100%; |
|
border-radius: 8px; |
|
object-fit: cover; |
|
} |
|
|
|
|
|
.image-item { |
|
width: 150px; |
|
height: 150px; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
overflow: hidden; |
|
border: 2px solid #ddd; |
|
border-radius: 8px; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.image-item:hover { |
|
border-color: #4CAF50; |
|
} |
|
|
|
|
|
.image-container { |
|
display: flex; |
|
justify-content: center; |
|
flex-wrap: wrap; |
|
gap: 10px; |
|
margin: 20px 0; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<h1>Labélisation d'Images avec SAM</h1> |
|
|
|
|
|
<section class="upload-section"> |
|
<form method="post" enctype="multipart/form-data"> |
|
<label for="image">Télécharger des images :</label><br> |
|
<input type="file" id="image" name="images" accept="image/*" multiple required> |
|
<br> |
|
<button type="submit">Télécharger les images</button> |
|
</form> |
|
</section> |
|
|
|
{% if uploaded_images %} |
|
|
|
<section> |
|
<h2>Images téléchargées</h2> |
|
<div class="image-container"> |
|
{% for image in uploaded_images %} |
|
<div class="image-item" onclick="loadImage('{{ image }}')"> |
|
<img src="{{ url_for('static', filename='uploads/' + image) }}" alt="{{ image }}"> |
|
</div> |
|
{% endfor %} |
|
</div> |
|
</section> |
|
|
|
|
|
<section> |
|
<canvas id="image-canvas"></canvas> |
|
</section> |
|
|
|
|
|
<section> |
|
<h2>Image Annotée</h2> |
|
<img id="annotated-image" style="display: none;" alt="Image Annotée"> |
|
</section> |
|
|
|
|
|
<section class="class-management"> |
|
<h3>Ajouter une classe :</h3> |
|
<input type="text" id="class-name" placeholder="Entrez une classe"> |
|
<button id="add-class">Ajouter</button> |
|
<h4>Classes disponibles :</h4> |
|
<ul id="class-list" class="class-list"></ul> |
|
</section> |
|
|
|
|
|
<section> |
|
<button id="finish-button" disabled>Terminer l'annotation</button> |
|
<button id="segment-button" disabled>Lancer la Segmentation</button> |
|
</section> |
|
{% endif %} |
|
|
|
<script> |
|
let canvas = document.getElementById('image-canvas'); |
|
let ctx = canvas ? canvas.getContext('2d') : null; |
|
let points = []; |
|
let currentClass = null; |
|
let img = null; |
|
let imgWidth = 0; |
|
let imgHeight = 0; |
|
|
|
{% if uploaded_images %} |
|
function loadImage(imageName) { |
|
|
|
points = []; |
|
document.getElementById('finish-button').disabled = false; |
|
document.getElementById('segment-button').disabled = true; |
|
|
|
|
|
img = new Image(); |
|
img.src = "{{ url_for('static', filename='uploads/') }}" + imageName; |
|
|
|
img.onload = () => { |
|
imgWidth = img.width; |
|
imgHeight = img.height; |
|
canvas.width = imgWidth; |
|
canvas.height = imgHeight; |
|
ctx.clearRect(0, 0, canvas.width, canvas.height); |
|
ctx.drawImage(img, 0, 0); |
|
}; |
|
} |
|
|
|
|
|
document.getElementById('add-class').addEventListener('click', () => { |
|
const className = document.getElementById('class-name').value.trim(); |
|
if (!className) return; |
|
|
|
const li = document.createElement('li'); |
|
li.textContent = className; |
|
li.classList.add('class-item'); |
|
li.onclick = () => { |
|
document.querySelectorAll('.class-item').forEach(item => item.classList.remove('active')); |
|
li.classList.add('active'); |
|
currentClass = className; |
|
}; |
|
document.getElementById('class-list').appendChild(li); |
|
|
|
document.getElementById('class-name').value = ''; |
|
}); |
|
|
|
|
|
canvas.addEventListener('click', event => { |
|
if (!currentClass) { |
|
alert('Veuillez sélectionner une classe.'); |
|
return; |
|
} |
|
|
|
const rect = canvas.getBoundingClientRect(); |
|
const x = event.clientX - rect.left; |
|
const y = event.clientY - rect.top; |
|
|
|
points.push({ x, y, class: currentClass }); |
|
|
|
ctx.fillStyle = 'red'; |
|
ctx.beginPath(); |
|
ctx.arc(x, y, 5, 0, 2 * Math.PI); |
|
ctx.fill(); |
|
|
|
|
|
document.getElementById('segment-button').disabled = points.length === 0; |
|
}); |
|
|
|
|
|
document.getElementById('finish-button').addEventListener('click', () => { |
|
alert("Annotation terminée pour cette image."); |
|
document.getElementById('finish-button').disabled = true; |
|
}); |
|
|
|
|
|
document.getElementById('segment-button').addEventListener('click', () => { |
|
fetch('/segment', { |
|
method: 'POST', |
|
headers: { 'Content-Type': 'application/json' }, |
|
body: JSON.stringify({ |
|
image_names: ["{{ uploaded_image }}"], |
|
points: points |
|
}) |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
if (data.success) { |
|
document.getElementById('annotated-image').src = "{{ url_for('static', filename='') }}" + data.annotated_image; |
|
document.getElementById('annotated-image').style.display = 'block'; |
|
} else { |
|
alert("Erreur : " + data.error); |
|
} |
|
}) |
|
.catch(err => { |
|
console.error('Erreur:', err); |
|
}); |
|
}); |
|
{% endif %} |
|
</script> |
|
</body> |
|
</html> |
|
|