Spaces:
Sleeping
Sleeping
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>๊ฐ์ฒด ์ธ์ ๊ฒฐ๊ณผ ๋ฒกํฐ ๊ฒ์</title> | |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> | |
<style> | |
body { | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
padding: 20px; | |
background-color: #f8f9fa; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
background-color: white; | |
padding: 20px; | |
border-radius: 10px; | |
box-shadow: 0 0 10px rgba(0,0,0,0.1); | |
} | |
.header { | |
margin-bottom: 30px; | |
text-align: center; | |
} | |
.upload-section, .detection-section, .search-section, .results-section { | |
margin-bottom: 30px; | |
padding: 20px; | |
border: 1px solid #dee2e6; | |
border-radius: 5px; | |
} | |
.canvas-container { | |
position: relative; | |
margin: 20px 0; | |
} | |
#imageCanvas { | |
border: 1px solid #ddd; | |
max-width: 100%; | |
} | |
.object-item { | |
margin-bottom: 10px; | |
padding: 10px; | |
border: 1px solid #e9ecef; | |
border-radius: 5px; | |
background-color: #f8f9fa; | |
} | |
.result-item { | |
display: flex; | |
margin-bottom: 20px; | |
padding: 15px; | |
border: 1px solid #e9ecef; | |
border-radius: 5px; | |
background-color: #f8f9fa; | |
} | |
.result-image { | |
width: 150px; | |
height: 150px; | |
object-fit: cover; | |
margin-right: 15px; | |
border: 1px solid #ddd; | |
} | |
.result-details { | |
flex-grow: 1; | |
} | |
.badge { | |
margin-right: 5px; | |
} | |
.nav-tabs { | |
margin-bottom: 20px; | |
} | |
.tab-content { | |
padding: 20px; | |
border: 1px solid #dee2e6; | |
border-top: none; | |
border-radius: 0 0 5px 5px; | |
} | |
.bbox-overlay { | |
position: absolute; | |
border: 2px solid; | |
background-color: rgba(255, 255, 255, 0.2); | |
pointer-events: none; | |
} | |
.bbox-label { | |
position: absolute; | |
background-color: rgba(0, 0, 0, 0.7); | |
color: white; | |
padding: 2px 6px; | |
border-radius: 3px; | |
font-size: 12px; | |
pointer-events: none; | |
} | |
.spinner-border { | |
width: 1rem; | |
height: 1rem; | |
margin-right: 0.5rem; | |
} | |
.loading { | |
display: none; | |
align-items: center; | |
margin-top: 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="header"> | |
<h1>๊ฐ์ฒด ์ธ์ ๊ฒฐ๊ณผ ๋ฒกํฐ ๊ฒ์</h1> | |
<p class="lead">์ด๋ฏธ์ง์์ ์ธ์๋ ๊ฐ์ฒด๋ฅผ ๋ฒกํฐ DB์ ์ ์ฅํ๊ณ ์ ์ฌํ ๊ฐ์ฒด๋ฅผ ๊ฒ์ํฉ๋๋ค.</p> | |
</div> | |
<ul class="nav nav-tabs" id="myTab" role="tablist"> | |
<li class="nav-item" role="presentation"> | |
<button class="nav-link active" id="detect-tab" data-bs-toggle="tab" data-bs-target="#detect" type="button" role="tab" aria-controls="detect" aria-selected="true">๊ฐ์ฒด ์ธ์ ๋ฐ ์ ์ฅ</button> | |
</li> | |
<li class="nav-item" role="presentation"> | |
<button class="nav-link" id="search-tab" data-bs-toggle="tab" data-bs-target="#search" type="button" role="tab" aria-controls="search" aria-selected="false">๊ฐ์ฒด ๊ฒ์</button> | |
</li> | |
</ul> | |
<div class="tab-content" id="myTabContent"> | |
<!-- ๊ฐ์ฒด ์ธ์ ๋ฐ ์ ์ฅ ํญ --> | |
<div class="tab-pane fade show active" id="detect" role="tabpanel" aria-labelledby="detect-tab"> | |
<div class="upload-section"> | |
<h3>์ด๋ฏธ์ง ์ ๋ก๋</h3> | |
<div class="mb-3"> | |
<input class="form-control" type="file" id="imageUpload" accept="image/*"> | |
</div> | |
<button id="detectObjectsBtn" class="btn btn-primary" disabled>๊ฐ์ฒด ์ธ์ํ๊ธฐ</button> | |
<div class="loading" id="detectLoading"> | |
<div class="spinner-border text-primary" role="status"></div> | |
<span>๊ฐ์ฒด ์ธ์ ์ค...</span> | |
</div> | |
</div> | |
<div class="detection-section"> | |
<h3>์ธ์ ๊ฒฐ๊ณผ</h3> | |
<div class="canvas-container"> | |
<canvas id="imageCanvas"></canvas> | |
<!-- ๋ฐ์ด๋ฉ ๋ฐ์ค์ ๋ผ๋ฒจ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ถ๊ฐ๋ฉ๋๋ค --> | |
</div> | |
<div id="detectedObjects" class="mt-3"> | |
<p>์ธ์๋ ๊ฐ์ฒด๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค.</p> | |
</div> | |
<button id="saveToVectorDBBtn" class="btn btn-success mt-3" disabled>๋ฒกํฐ DB์ ์ ์ฅํ๊ธฐ</button> | |
<div class="loading" id="saveLoading"> | |
<div class="spinner-border text-success" role="status"></div> | |
<span>๋ฒกํฐ DB์ ์ ์ฅ ์ค...</span> | |
</div> | |
</div> | |
</div> | |
<!-- ๊ฐ์ฒด ๊ฒ์ ํญ --> | |
<div class="tab-pane fade" id="search" role="tabpanel" aria-labelledby="search-tab"> | |
<div class="search-section"> | |
<h3>๊ฒ์ ๋ฐฉ๋ฒ</h3> | |
<div class="mb-3"> | |
<select class="form-select" id="searchType"> | |
<option value="image">์ด๋ฏธ์ง๋ก ๊ฒ์</option> | |
<option value="class">ํด๋์ค๋ก ๊ฒ์</option> | |
</select> | |
</div> | |
<div id="imageSearchSection"> | |
<div class="mb-3"> | |
<label for="searchImageUpload" class="form-label">๊ฒ์ํ ์ด๋ฏธ์ง ์ ๋ก๋</label> | |
<input class="form-control" type="file" id="searchImageUpload" accept="image/*"> | |
</div> | |
</div> | |
<div id="classSearchSection" style="display: none;"> | |
<div class="mb-3"> | |
<label for="classNameInput" class="form-label">ํด๋์ค ์ด๋ฆ</label> | |
<input type="text" class="form-control" id="classNameInput" placeholder="์: person, car, dog ๋ฑ"> | |
</div> | |
</div> | |
<div class="mb-3"> | |
<label for="resultCount" class="form-label">๊ฒ์ ๊ฒฐ๊ณผ ์</label> | |
<input type="number" class="form-control" id="resultCount" min="1" max="20" value="5"> | |
</div> | |
<button id="searchBtn" class="btn btn-primary">๊ฒ์ํ๊ธฐ</button> | |
<div class="loading" id="searchLoading"> | |
<div class="spinner-border text-primary" role="status"></div> | |
<span>๊ฒ์ ์ค...</span> | |
</div> | |
</div> | |
<div class="results-section"> | |
<h3>๊ฒ์ ๊ฒฐ๊ณผ</h3> | |
<div id="searchResults"> | |
<p>๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค.</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> | |
<script> | |
// ์ ์ญ ๋ณ์ | |
let currentImage = null; | |
let detectedObjects = []; | |
let imageWidth = 0; | |
let imageHeight = 0; | |
const colors = ['#FF5733', '#33FF57', '#3357FF', '#F033FF', '#FF3333', '#33FFFF', '#FFFF33']; | |
// DOM ์์ | |
const imageUpload = document.getElementById('imageUpload'); | |
const detectObjectsBtn = document.getElementById('detectObjectsBtn'); | |
const imageCanvas = document.getElementById('imageCanvas'); | |
const ctx = imageCanvas.getContext('2d'); | |
const detectedObjectsDiv = document.getElementById('detectedObjects'); | |
const saveToVectorDBBtn = document.getElementById('saveToVectorDBBtn'); | |
const searchType = document.getElementById('searchType'); | |
const imageSearchSection = document.getElementById('imageSearchSection'); | |
const classSearchSection = document.getElementById('classSearchSection'); | |
const searchImageUpload = document.getElementById('searchImageUpload'); | |
const classNameInput = document.getElementById('classNameInput'); | |
const resultCount = document.getElementById('resultCount'); | |
const searchBtn = document.getElementById('searchBtn'); | |
const searchResults = document.getElementById('searchResults'); | |
// ๋ก๋ฉ ํ์ | |
const detectLoading = document.getElementById('detectLoading'); | |
const saveLoading = document.getElementById('saveLoading'); | |
const searchLoading = document.getElementById('searchLoading'); | |
// ์ด๋ฏธ์ง ์ ๋ก๋ ์ฒ๋ฆฌ | |
imageUpload.addEventListener('change', function(e) { | |
const file = e.target.files[0]; | |
if (!file) return; | |
const reader = new FileReader(); | |
reader.onload = function(event) { | |
const img = new Image(); | |
img.onload = function() { | |
// ์บ๋ฒ์ค ํฌ๊ธฐ ์ค์ | |
imageWidth = img.width; | |
imageHeight = img.height; | |
// ์บ๋ฒ์ค ํฌ๊ธฐ๋ฅผ ์ด๋ฏธ์ง์ ๋ง๊ฒ ์กฐ์ ํ๋, ์ต๋ ๋๋น ์ ํ | |
const maxWidth = 800; | |
let displayWidth = img.width; | |
let displayHeight = img.height; | |
if (displayWidth > maxWidth) { | |
const ratio = maxWidth / displayWidth; | |
displayWidth = maxWidth; | |
displayHeight = displayHeight * ratio; | |
} | |
imageCanvas.width = displayWidth; | |
imageCanvas.height = displayHeight; | |
// ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๊ธฐ | |
ctx.drawImage(img, 0, 0, displayWidth, displayHeight); | |
// ํ์ฌ ์ด๋ฏธ์ง ์ ์ฅ | |
currentImage = event.target.result; | |
// ๊ฐ์ฒด ์ธ์ ๋ฒํผ ํ์ฑํ | |
detectObjectsBtn.disabled = false; | |
// ์ด์ ๊ฐ์ฒด ์ธ์ ๊ฒฐ๊ณผ ์ด๊ธฐํ | |
detectedObjects = []; | |
detectedObjectsDiv.innerHTML = '<p>์ธ์๋ ๊ฐ์ฒด๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค.</p>'; | |
saveToVectorDBBtn.disabled = true; | |
// ๋ฐ์ด๋ฉ ๋ฐ์ค ์ ๊ฑฐ | |
clearBoundingBoxes(); | |
}; | |
img.src = event.target.result; | |
}; | |
reader.readAsDataURL(file); | |
}); | |
// ๊ฐ์ฒด ์ธ์ ์ฒ๋ฆฌ | |
detectObjectsBtn.addEventListener('click', function() { | |
if (!currentImage) return; | |
// ๋ก๋ฉ ํ์ | |
detectLoading.style.display = 'flex'; | |
detectObjectsBtn.disabled = true; | |
// ๊ฐ์ฒด ์ธ์ API ํธ์ถ | |
fetch('/api/detect', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
image: currentImage, | |
model: 'yolo' // ๊ธฐ๋ณธ ๋ชจ๋ธ๋ก YOLO ์ฌ์ฉ | |
}) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// ๋ก๋ฉ ์จ๊ธฐ๊ธฐ | |
detectLoading.style.display = 'none'; | |
detectObjectsBtn.disabled = false; | |
if (data.error) { | |
alert('๊ฐ์ฒด ์ธ์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: ' + data.error); | |
return; | |
} | |
// ์ธ์๋ ๊ฐ์ฒด ์ ์ฅ | |
detectedObjects = data.objects || []; | |
// ๊ฒฐ๊ณผ๊ฐ ์๋ ๊ฒฝ์ฐ | |
if (detectedObjects.length === 0) { | |
detectedObjectsDiv.innerHTML = '<p>์ธ์๋ ๊ฐ์ฒด๊ฐ ์์ต๋๋ค.</p>'; | |
saveToVectorDBBtn.disabled = true; | |
return; | |
} | |
// ๋ฐ์ด๋ฉ ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ | |
drawBoundingBoxes(); | |
// ์ธ์๋ ๊ฐ์ฒด ๋ชฉ๋ก ํ์ | |
displayDetectedObjects(); | |
// ๋ฒกํฐ DB ์ ์ฅ ๋ฒํผ ํ์ฑํ | |
saveToVectorDBBtn.disabled = false; | |
}) | |
.catch(error => { | |
detectLoading.style.display = 'none'; | |
detectObjectsBtn.disabled = false; | |
console.error('Error:', error); | |
alert('๊ฐ์ฒด ์ธ์ ์์ฒญ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.'); | |
}); | |
}); | |
// ๋ฐ์ด๋ฉ ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ ํจ์ | |
function drawBoundingBoxes() { | |
// ์บ๋ฒ์ค ํฌ๊ธฐ ๊ฐ์ ธ์ค๊ธฐ | |
const canvasWidth = imageCanvas.width; | |
const canvasHeight = imageCanvas.height; | |
// ๊ธฐ์กด ๋ฐ์ด๋ฉ ๋ฐ์ค ์ ๊ฑฐ | |
clearBoundingBoxes(); | |
// ์บ๋ฒ์ค ์ปจํ ์ด๋ ๊ฐ์ ธ์ค๊ธฐ | |
const canvasContainer = document.querySelector('.canvas-container'); | |
// ๊ฐ ๊ฐ์ฒด์ ๋ํด ๋ฐ์ด๋ฉ ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ | |
detectedObjects.forEach((obj, index) => { | |
const bbox = obj.bbox; | |
const colorIndex = index % colors.length; | |
// ์๋ ์ขํ๋ฅผ ์บ๋ฒ์ค ์ขํ๋ก ๋ณํ | |
const x = bbox.x * canvasWidth; | |
const y = bbox.y * canvasHeight; | |
const width = bbox.width * canvasWidth; | |
const height = bbox.height * canvasHeight; | |
// ๋ฐ์ด๋ฉ ๋ฐ์ค ์์ ์์ฑ | |
const boxElement = document.createElement('div'); | |
boxElement.className = 'bbox-overlay'; | |
boxElement.style.left = `${x}px`; | |
boxElement.style.top = `${y}px`; | |
boxElement.style.width = `${width}px`; | |
boxElement.style.height = `${height}px`; | |
boxElement.style.borderColor = colors[colorIndex]; | |
// ๋ผ๋ฒจ ์์ ์์ฑ | |
const labelElement = document.createElement('div'); | |
labelElement.className = 'bbox-label'; | |
labelElement.style.left = `${x}px`; | |
labelElement.style.top = `${y - 20}px`; | |
labelElement.style.backgroundColor = colors[colorIndex]; | |
labelElement.textContent = `${obj.class} ${Math.round(obj.confidence * 100)}%`; | |
// ์์๋ฅผ ์บ๋ฒ์ค ์ปจํ ์ด๋์ ์ถ๊ฐ | |
canvasContainer.appendChild(boxElement); | |
canvasContainer.appendChild(labelElement); | |
}); | |
} | |
// ๋ฐ์ด๋ฉ ๋ฐ์ค ์ ๊ฑฐ ํจ์ | |
function clearBoundingBoxes() { | |
const overlays = document.querySelectorAll('.bbox-overlay, .bbox-label'); | |
overlays.forEach(overlay => overlay.remove()); | |
} | |
// ์ธ์๋ ๊ฐ์ฒด ๋ชฉ๋ก ํ์ ํจ์ | |
function displayDetectedObjects() { | |
let html = '<h4>์ธ์๋ ๊ฐ์ฒด ๋ชฉ๋ก:</h4><ul class="list-group">'; | |
detectedObjects.forEach((obj, index) => { | |
const colorIndex = index % colors.length; | |
html += ` | |
<li class="list-group-item object-item"> | |
<div class="d-flex justify-content-between align-items-center"> | |
<div> | |
<span class="badge bg-primary">${index + 1}</span> | |
<span class="badge" style="background-color: ${colors[colorIndex]}">${obj.class}</span> | |
<span>์ ๋ขฐ๋: ${Math.round(obj.confidence * 100)}%</span> | |
</div> | |
</div> | |
</li> | |
`; | |
}); | |
html += '</ul>'; | |
detectedObjectsDiv.innerHTML = html; | |
} | |
// ๋ฒกํฐ DB์ ์ ์ฅ ์ฒ๋ฆฌ | |
saveToVectorDBBtn.addEventListener('click', function() { | |
if (!currentImage || detectedObjects.length === 0) return; | |
// ๋ก๋ฉ ํ์ | |
saveLoading.style.display = 'flex'; | |
saveToVectorDBBtn.disabled = true; | |
// ๋ฒกํฐ DB ์ ์ฅ API ํธ์ถ | |
fetch('/api/add-detected-objects', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
image: currentImage, | |
objects: detectedObjects | |
}) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// ๋ก๋ฉ ์จ๊ธฐ๊ธฐ | |
saveLoading.style.display = 'none'; | |
saveToVectorDBBtn.disabled = false; | |
if (data.error) { | |
alert('๋ฒกํฐ DB ์ ์ฅ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: ' + data.error); | |
return; | |
} | |
alert(`์ฑ๊ณต์ ์ผ๋ก ${data.object_count}๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ฒกํฐ DB์ ์ ์ฅํ์ต๋๋ค.`); | |
}) | |
.catch(error => { | |
saveLoading.style.display = 'none'; | |
saveToVectorDBBtn.disabled = false; | |
console.error('Error:', error); | |
alert('๋ฒกํฐ DB ์ ์ฅ ์์ฒญ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.'); | |
}); | |
}); | |
// ๊ฒ์ ์ ํ ๋ณ๊ฒฝ ์ฒ๋ฆฌ | |
searchType.addEventListener('change', function() { | |
if (this.value === 'image') { | |
imageSearchSection.style.display = 'block'; | |
classSearchSection.style.display = 'none'; | |
} else if (this.value === 'class') { | |
imageSearchSection.style.display = 'none'; | |
classSearchSection.style.display = 'block'; | |
} | |
}); | |
// ๊ฒ์ ์ด๋ฏธ์ง ์ ๋ก๋ ์ฒ๋ฆฌ | |
searchImageUpload.addEventListener('change', function(e) { | |
const file = e.target.files[0]; | |
if (!file) return; | |
const reader = new FileReader(); | |
reader.onload = function(event) { | |
// ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ ์ฅ | |
searchImageUpload.dataset.image = event.target.result; | |
}; | |
reader.readAsDataURL(file); | |
}); | |
// ๊ฒ์ ์ฒ๋ฆฌ | |
searchBtn.addEventListener('click', function() { | |
// ๋ก๋ฉ ํ์ | |
searchLoading.style.display = 'flex'; | |
searchBtn.disabled = true; | |
// ๊ฒ์ ์ ํ์ ๋ฐ๋ผ ์์ฒญ ๋ฐ์ดํฐ ๊ตฌ์ฑ | |
const searchTypeValue = searchType.value; | |
const nResults = parseInt(resultCount.value) || 5; | |
let requestData = { | |
searchType: searchTypeValue, | |
nResults: nResults | |
}; | |
if (searchTypeValue === 'image') { | |
const imageData = searchImageUpload.dataset.image; | |
if (!imageData) { | |
alert('๊ฒ์ํ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํด์ฃผ์ธ์.'); | |
searchLoading.style.display = 'none'; | |
searchBtn.disabled = false; | |
return; | |
} | |
requestData.image = imageData; | |
} else if (searchTypeValue === 'class') { | |
const className = classNameInput.value.trim(); | |
if (!className) { | |
alert('๊ฒ์ํ ํด๋์ค ์ด๋ฆ์ ์ ๋ ฅํด์ฃผ์ธ์.'); | |
searchLoading.style.display = 'none'; | |
searchBtn.disabled = false; | |
return; | |
} | |
requestData.className = className; | |
} | |
// ๊ฒ์ API ํธ์ถ | |
fetch('/api/search-similar-objects', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify(requestData) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// ๋ก๋ฉ ์จ๊ธฐ๊ธฐ | |
searchLoading.style.display = 'none'; | |
searchBtn.disabled = false; | |
if (data.error) { | |
alert('๊ฒ์ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: ' + data.error); | |
return; | |
} | |
// ๊ฒ์ ๊ฒฐ๊ณผ ํ์ | |
displaySearchResults(data.results); | |
}) | |
.catch(error => { | |
searchLoading.style.display = 'none'; | |
searchBtn.disabled = false; | |
console.error('Error:', error); | |
alert('๊ฒ์ ์์ฒญ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.'); | |
}); | |
}); | |
// ๊ฒ์ ๊ฒฐ๊ณผ ํ์ ํจ์ | |
function displaySearchResults(results) { | |
if (!results || results.length === 0) { | |
searchResults.innerHTML = '<p>๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์์ต๋๋ค.</p>'; | |
return; | |
} | |
let html = '<div class="row">'; | |
results.forEach((result, index) => { | |
const metadata = result.metadata || {}; | |
const distance = result.distance ? (1 - result.distance).toFixed(2) * 100 : 0; | |
const imageId = metadata.image_id || ''; | |
const className = metadata.class || ''; | |
const confidence = metadata.confidence ? Math.round(metadata.confidence * 100) : 0; | |
const bbox = metadata.bbox || {}; | |
html += ` | |
<div class="col-md-6 mb-3"> | |
<div class="card"> | |
<div class="card-body"> | |
<h5 class="card-title">๊ฒฐ๊ณผ #${index + 1}</h5> | |
<div class="d-flex justify-content-between"> | |
<span class="badge bg-primary">${className}</span> | |
<span class="badge bg-info">์ ์ฌ๋: ${distance}%</span> | |
</div> | |
<p class="card-text mt-2"> | |
<small>๊ฐ์ฒด ID: ${result.id}</small><br> | |
<small>์ด๋ฏธ์ง ID: ${imageId}</small><br> | |
<small>์ ๋ขฐ๋: ${confidence}%</small><br> | |
<small>์์น: X=${(bbox.x * 100).toFixed(1)}%, Y=${(bbox.y * 100).toFixed(1)}%, W=${(bbox.width * 100).toFixed(1)}%, H=${(bbox.height * 100).toFixed(1)}%</small> | |
</p> | |
</div> | |
</div> | |
</div> | |
`; | |
}); | |
html += '</div>'; | |
searchResults.innerHTML = html; | |
} | |
</script> | |
</body> | |
</html> | |