Spaces:
Runtime error
Runtime error
David Ko
์ ์ฌ ์ด๋ฏธ์ง ๊ฒ์ ๊ธฐ๋ฅ ์ถ๊ฐ: CLIP ๋ชจ๋ธ๊ณผ ChromaDB ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ์ฉ
9d90c9e
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>์ ์ฌ ์ด๋ฏธ์ง ๊ฒ์</title> | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"> | |
| <style> | |
| .image-container { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 15px; | |
| margin-top: 20px; | |
| } | |
| .image-card { | |
| border: 1px solid #ddd; | |
| border-radius: 8px; | |
| padding: 10px; | |
| width: 220px; | |
| } | |
| .image-preview { | |
| width: 200px; | |
| height: 200px; | |
| object-fit: cover; | |
| border-radius: 4px; | |
| margin-bottom: 10px; | |
| } | |
| .spinner-border { | |
| display: none; | |
| } | |
| .result-container { | |
| margin-top: 30px; | |
| } | |
| .similar-image { | |
| width: 150px; | |
| height: 150px; | |
| object-fit: cover; | |
| border-radius: 4px; | |
| } | |
| .similar-item { | |
| margin-bottom: 15px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container mt-5"> | |
| <h1 class="mb-4">์ ์ฌ ์ด๋ฏธ์ง ๊ฒ์</h1> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h5>์ด๋ฏธ์ง ์ ๋ก๋</h5> | |
| </div> | |
| <div class="card-body"> | |
| <form id="uploadForm"> | |
| <div class="mb-3"> | |
| <label for="imageInput" class="form-label">์ด๋ฏธ์ง ์ ํ</label> | |
| <input type="file" class="form-control" id="imageInput" accept="image/*"> | |
| </div> | |
| <div class="mb-3"> | |
| <div class="form-check"> | |
| <input class="form-check-input" type="checkbox" id="addToCollection"> | |
| <label class="form-check-label" for="addToCollection"> | |
| ์ปฌ๋ ์ ์ ์ด๋ฏธ์ง ์ถ๊ฐ | |
| </label> | |
| </div> | |
| </div> | |
| <button type="submit" class="btn btn-primary"> | |
| <span class="spinner-border spinner-border-sm" id="searchSpinner" role="status" aria-hidden="true"></span> | |
| ์ ์ฌ ์ด๋ฏธ์ง ๊ฒ์ | |
| </button> | |
| </form> | |
| <div class="mt-3"> | |
| <div id="previewContainer" style="display: none;"> | |
| <h6>์ ๋ก๋๋ ์ด๋ฏธ์ง:</h6> | |
| <img id="imagePreview" class="image-preview" src="" alt="Preview"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="card mt-4"> | |
| <div class="card-header"> | |
| <h5>์ํ ์ด๋ฏธ์ง ์ถ๊ฐ</h5> | |
| </div> | |
| <div class="card-body"> | |
| <p>๋ฒกํฐ DB์ ์ํ ์ด๋ฏธ์ง๋ฅผ ์ถ๊ฐํฉ๋๋ค.</p> | |
| <button id="addSamplesBtn" class="btn btn-secondary"> | |
| <span class="spinner-border spinner-border-sm" id="sampleSpinner" role="status" aria-hidden="true"></span> | |
| ์ํ ์ด๋ฏธ์ง ์ถ๊ฐ | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="col-md-6"> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h5>๊ฒ์ ๊ฒฐ๊ณผ</h5> | |
| </div> | |
| <div class="card-body"> | |
| <div id="resultsContainer"> | |
| <p id="noResults">๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค.</p> | |
| <div id="similarImagesContainer" class="row"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ | |
| document.getElementById('imageInput').addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = function(event) { | |
| document.getElementById('imagePreview').src = event.target.result; | |
| document.getElementById('previewContainer').style.display = 'block'; | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| }); | |
| // ํผ ์ ์ถ ์ฒ๋ฆฌ | |
| document.getElementById('uploadForm').addEventListener('submit', async function(e) { | |
| e.preventDefault(); | |
| const fileInput = document.getElementById('imageInput'); | |
| const addToCollection = document.getElementById('addToCollection').checked; | |
| if (!fileInput.files[0]) { | |
| alert('์ด๋ฏธ์ง๋ฅผ ์ ํํด์ฃผ์ธ์.'); | |
| return; | |
| } | |
| // ๋ก๋ฉ ํ์ | |
| document.getElementById('searchSpinner').style.display = 'inline-block'; | |
| const formData = new FormData(); | |
| formData.append('image', fileInput.files[0]); | |
| try { | |
| // ์ปฌ๋ ์ ์ ์ถ๊ฐ ์ต์ ์ด ์ ํ๋ ๊ฒฝ์ฐ | |
| if (addToCollection) { | |
| const addResponse = await fetch('/api/add-to-collection', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const addResult = await addResponse.json(); | |
| console.log('Add to collection result:', addResult); | |
| } | |
| // ์ ์ฌ ์ด๋ฏธ์ง ๊ฒ์ | |
| const searchResponse = await fetch('/api/similar-images', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const searchResult = await searchResponse.json(); | |
| console.log('Search result:', searchResult); | |
| // ๊ฒฐ๊ณผ ํ์ | |
| displayResults(searchResult); | |
| } catch (error) { | |
| console.error('Error:', error); | |
| alert('์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: ' + error.message); | |
| } finally { | |
| // ๋ก๋ฉ ํ์ ์ ๊ฑฐ | |
| document.getElementById('searchSpinner').style.display = 'none'; | |
| } | |
| }); | |
| // ๊ฒฐ๊ณผ ํ์ ํจ์ | |
| function displayResults(results) { | |
| const container = document.getElementById('similarImagesContainer'); | |
| const noResults = document.getElementById('noResults'); | |
| container.innerHTML = ''; | |
| if (results.error) { | |
| noResults.textContent = '์ค๋ฅ: ' + results.error; | |
| noResults.style.display = 'block'; | |
| return; | |
| } | |
| if (!results.similar_images || results.similar_images.length === 0) { | |
| noResults.textContent = '์ ์ฌํ ์ด๋ฏธ์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ๋จผ์ ์ด๋ฏธ์ง๋ฅผ ์ปฌ๋ ์ ์ ์ถ๊ฐํด๋ณด์ธ์.'; | |
| noResults.style.display = 'block'; | |
| return; | |
| } | |
| noResults.style.display = 'none'; | |
| results.similar_images.forEach((item, index) => { | |
| const col = document.createElement('div'); | |
| col.className = 'col-6 similar-item'; | |
| const card = document.createElement('div'); | |
| card.className = 'card h-100'; | |
| // ์ด๋ฏธ์ง URL์ด ๋ฉํ๋ฐ์ดํฐ์ ์๋ ๊ฒฝ์ฐ | |
| let imageUrl = ''; | |
| if (item.metadata && item.metadata.url) { | |
| imageUrl = item.metadata.url; | |
| } else { | |
| // ์ค์ ๊ตฌํ์์๋ ์ด๋ฏธ์ง ID๋ก ์ด๋ฏธ์ง๋ฅผ ๊ฐ์ ธ์ค๋ API๊ฐ ํ์ํ ์ ์์ | |
| imageUrl = 'https://via.placeholder.com/150?text=Image+' + (index + 1); | |
| } | |
| const distance = item.distance ? item.distance.toFixed(4) : 'N/A'; | |
| card.innerHTML = ` | |
| <img src="${imageUrl}" class="similar-image card-img-top" alt="Similar Image ${index + 1}"> | |
| <div class="card-body"> | |
| <h6 class="card-title">์ ์ฌ๋: ${distance}</h6> | |
| <p class="card-text">ID: ${item.id.substring(0, 8)}...</p> | |
| </div> | |
| `; | |
| col.appendChild(card); | |
| container.appendChild(col); | |
| }); | |
| } | |
| // ์ํ ์ด๋ฏธ์ง ์ถ๊ฐ | |
| document.getElementById('addSamplesBtn').addEventListener('click', async function() { | |
| const spinner = document.getElementById('sampleSpinner'); | |
| spinner.style.display = 'inline-block'; | |
| try { | |
| // ์ํ ์ด๋ฏธ์ง URL ๋ฐฐ์ด (์ค์ ๊ตฌํ์์๋ ์ ์ ํ ์ด๋ฏธ์ง๋ก ๋ณ๊ฒฝ) | |
| const sampleImages = [ | |
| { url: 'https://source.unsplash.com/random/300x300?cat', label: 'cat' }, | |
| { url: 'https://source.unsplash.com/random/300x300?dog', label: 'dog' }, | |
| { url: 'https://source.unsplash.com/random/300x300?bird', label: 'bird' }, | |
| { url: 'https://source.unsplash.com/random/300x300?flower', label: 'flower' }, | |
| { url: 'https://source.unsplash.com/random/300x300?car', label: 'car' } | |
| ]; | |
| for (const sample of sampleImages) { | |
| // ์ด๋ฏธ์ง ๊ฐ์ ธ์ค๊ธฐ | |
| const response = await fetch(sample.url); | |
| const blob = await response.blob(); | |
| // FormData ์์ฑ | |
| const formData = new FormData(); | |
| formData.append('image', blob, 'sample.jpg'); | |
| formData.append('metadata', JSON.stringify({ | |
| label: sample.label, | |
| url: sample.url | |
| })); | |
| // API ํธ์ถ | |
| const addResponse = await fetch('/api/add-to-collection', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const result = await addResponse.json(); | |
| console.log(`Added sample ${sample.label}:`, result); | |
| } | |
| alert('5๊ฐ์ ์ํ ์ด๋ฏธ์ง๊ฐ ์ปฌ๋ ์ ์ ์ถ๊ฐ๋์์ต๋๋ค.'); | |
| } catch (error) { | |
| console.error('Error adding samples:', error); | |
| alert('์ํ ์ด๋ฏธ์ง ์ถ๊ฐ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: ' + error.message); | |
| } finally { | |
| spinner.style.display = 'none'; | |
| } | |
| }); | |
| </script> | |
| <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> | |
| </body> | |
| </html> | |