document.addEventListener("DOMContentLoaded", () => { const imageInput = document.getElementById("imageInput"); const previewScroll = document.getElementById("preview-scroll"); const resultSection = document.getElementById("result-section"); const selectedImage = document.getElementById("selected-image"); const genreResults = document.getElementById("genre-results"); const styleResults = document.getElementById("style-results"); const loader = document.getElementById("loader"); const backgroundBlur = document.getElementById("background-blur"); let previews = []; imageInput.addEventListener("change", () => { previewScroll.innerHTML = ""; previews = []; Array.from(imageInput.files).forEach((file, index) => { const reader = new FileReader(); reader.onload = e => { const box = document.createElement("div"); box.className = "image-box"; const img = document.createElement("img"); img.src = e.target.result; img.className = "preview-image"; img.onclick = () => { previews.forEach(p => p.img.classList.remove("selected")); img.classList.add("selected"); showResult(img.src); analyzeImage(file); backgroundBlur.style.backgroundImage = `url(${img.src})`; }; const label = document.createElement("small"); label.className = "filename"; label.textContent = file.name; box.appendChild(img); box.appendChild(label); previewScroll.appendChild(box); previews.push({ img, file }); }; reader.readAsDataURL(file); }); }); function showResult(imgSrc) { resultSection.style.display = "block"; selectedImage.src = imgSrc; resetCamState(); } function analyzeImage(file) { const genreColors = ['#00ff88', '#00c4ff', '#ffaa00', '#ff5555', '#a0f', '#f0f']; const styleColors = ['#00c4ff', '#00ff88', '#ffcc00', '#f06', '#0ff', '#f99']; const paletteDiv = document.getElementById("color-palette"); const formData = new FormData(); formData.append("file", file); paletteDiv.innerHTML = ""; loader.style.display = "block"; fetch("/predict_all_combined", { method: "POST", body: formData }) .then(async res => { if (!res.ok) { const text = await res.text(); throw new Error(`Сервер вернул ${res.status}: ${text}`); } return res.json(); }) .then(data => { genreResults.innerHTML = ""; styleResults.innerHTML = ""; // Примеры миниатюр window.styleExampleMap = {}; data.style_top3.forEach(s => { window.styleExampleMap[s.label] = s.examples; }); window.genreExampleMap = {}; data.genre_top3.forEach(g => { window.genreExampleMap[g.label] = g.examples; }); // Диаграммы renderDonutChart("genreChart", data.genre_top3, genreColors, "Жанры"); renderDonutChart("styleChart", data.style_top3, styleColors, "Стили"); // Топ-10 жанров data.genre_top3.slice(0, 10).forEach(g => { genreResults.innerHTML += `
${g.label} — ${g.confidence}%
`; }); // Палитра data.palette.forEach(hex => { const swatch = document.createElement("div"); swatch.style.backgroundColor = hex; swatch.style.width = "30px"; swatch.style.height = "30px"; swatch.style.border = "1px solid #444"; swatch.style.cursor = "pointer"; swatch.title = `Нажмите, чтобы скопировать ${hex}`; swatch.onclick = () => { navigator.clipboard.writeText(hex).then(() => { swatch.title = `Скопировано: ${hex}`; swatch.style.outline = "2px solid #00ff88"; setTimeout(() => { swatch.title = `Нажмите, чтобы скопировать ${hex}`; swatch.style.outline = "none"; }, 1200); }); }; paletteDiv.appendChild(swatch); }); // Топ-10 стилей data.style_top3.slice(0, 10).forEach(s => { styleResults.innerHTML += `
${s.label} — ${s.confidence}%
`; }); // CAM-карта if (data.cam_path) { const camImg = document.getElementById("cam-image"); camImg.src = `/tmp/${data.cam_path}?t=${Date.now()}`; camImg.style.display = "block"; } // Гистограммы if (data.saturation_hist && data.brightness_hist) { document.getElementById("saturation-plot").src = `/tmp/${data.saturation_hist}?t=${Date.now()}`; document.getElementById("brightness-plot").src = `/tmp/${data.brightness_hist}?t=${Date.now()}`; } if (data.metadata && data.metadata.cdf_path) { document.getElementById("cdf-plot").src = `/tmp/${data.metadata.cdf_path}?t=${Date.now()}`; } if (data.metadata && data.metadata.pdf_path) { document.getElementById("pdf-plot").src = `/tmp/${data.metadata.pdf_path}?t=${Date.now()}`; } if (data.metadata && data.metadata.hog_path) { document.getElementById("hog-plot").src = `/tmp/${data.metadata.hog_path}?t=${Date.now()}`; } if (data.metadata.lbp_path) { document.getElementById("lbp-plot").src = `/tmp/${data.metadata.lbp_path}?t=${Date.now()}`; } if (data.metadata.orb_path) { document.getElementById("orb-plot").src = `/tmp/${data.metadata.orb_path}?t=${Date.now()}`; } // Семантический граф const genreThreshold = 0.05; const styleThreshold = 0.05; const allGenres = data.genre_top3_full || data.genre_top3; const allStyles = data.style_top3_full || data.style_top3; const fullNodes = [ { name: 'Изображение', category: 0, symbolSize: 100, itemStyle: { color: '#ffaa00' } } ]; const fullLinks = []; allGenres.forEach(g => { const conf = parseFloat(g.confidence); fullNodes.push({ name: g.label, category: 1, symbolSize: conf > genreThreshold ? 45 + conf / 2 : 20, value: conf, itemStyle: { color: conf > genreThreshold ? '#00ff88' : '#555', opacity: conf > genreThreshold ? 1 : 0.5 }, label: { color: conf > genreThreshold ? '#eee' : '#888' } }); fullLinks.push({ source: 'Изображение', target: g.label }); }); allStyles.forEach(s => { const conf = parseFloat(s.confidence); fullNodes.push({ name: s.label, category: 2, symbolSize: conf > styleThreshold ? 45 + conf / 2 : 20, value: conf, itemStyle: { color: conf > styleThreshold ? '#00c4ff' : '#444', opacity: conf > styleThreshold ? 1 : 0.5 }, label: { color: conf > styleThreshold ? '#eee' : '#888' } }); fullLinks.push({ source: 'Изображение', target: s.label }); }); const graphChart = echarts.init(document.getElementById('graph-container')); // Метаданные if (data.metadata) { const metaBlock = document.getElementById("image-meta-info"); metaBlock.innerHTML = ""; const baseInfo = document.createElement("div"); baseInfo.style.marginBottom = "12px"; baseInfo.innerHTML = `

Размер: ${data.metadata.width} × ${data.metadata.height}

Средняя насыщенность: ${data.metadata.avg_saturation}

Средняя яркость: ${data.metadata.avg_brightness}

Контраст: ${data.metadata.contrast}

`; metaBlock.appendChild(baseInfo); } if (data.metadata && data.metadata.color_statistics) { const statBlock = document.createElement("div"); statBlock.style.marginTop = "20px"; statBlock.style.background = "#111"; statBlock.style.border = "1px solid #333"; statBlock.style.padding = "12px"; statBlock.style.borderRadius = "6px"; statBlock.style.color = "#ccc"; statBlock.style.fontSize = "13px"; const rgb = data.metadata.color_statistics.rgb; const hsv = data.metadata.color_statistics.hsv; statBlock.innerHTML = `

RGB статистика

Среднее: R=${rgb.mean.R.toFixed(4)}, G=${rgb.mean.G.toFixed(4)}, B=${rgb.mean.B.toFixed(4)}

Дисперсия: R=${rgb.variance.R.toFixed(4)}, G=${rgb.variance.G.toFixed(4)}, B=${rgb.variance.B.toFixed(4)}

Корреляции: R-G=${rgb.correlation.RG.toFixed(4)}, R-B=${rgb.correlation.RB.toFixed(4)}, G-B=${rgb.correlation.GB.toFixed(4)}

HSV статистика

Среднее: H=${hsv.mean.H.toFixed(4)}, S=${hsv.mean.S.toFixed(4)}, V=${hsv.mean.V.toFixed(4)}

Дисперсия: H=${hsv.variance.H.toFixed(4)}, S=${hsv.variance.S.toFixed(4)}, V=${hsv.variance.V.toFixed(4)}

Корреляции: H-S=${hsv.correlation.HS.toFixed(4)}, H-V=${hsv.correlation.HV.toFixed(4)}, S-V=${hsv.correlation.SV.toFixed(4)}

`; const metaBlock = document.getElementById("image-meta-info"); metaBlock.appendChild(statBlock); } if (data.metadata && data.metadata.brightness_stats) { const stats = data.metadata.brightness_stats; const statEl = document.getElementById("brightness-statistics"); statEl.innerHTML = `

Среднее значение (Mean): ${stats.mean.toFixed(4)}

Медиана (Median): ${stats.median.toFixed(4)}

Минимум: ${stats.min.toFixed(4)}

Максимум: ${stats.max.toFixed(4)}

Стандартное отклонение: ${stats.std.toFixed(4)}

Мода (наиболее частое значение): ${stats.mode.toFixed(4)}

Энергия (сумма квадратов): ${stats.energy.toFixed(4)}

Энтропия (сложность распределения): ${stats.entropy.toFixed(4)}

`; } if (data.metadata && data.metadata.texture_features) { const t = data.metadata.texture_features; const texBlock = document.createElement("div"); texBlock.style.marginTop = "20px"; texBlock.style.background = "#111"; texBlock.style.border = "1px solid #333"; texBlock.style.padding = "12px"; texBlock.style.borderRadius = "6px"; texBlock.style.color = "#ccc"; texBlock.style.fontSize = "13px"; texBlock.innerHTML = `

Текстурные характеристики (GLCM)

Контраст: ${t.contrast.toFixed(4)}

Гомогенность: ${t.homogeneity.toFixed(4)}

Энергия: ${t.energy.toFixed(4)}

Энтропия: ${t.entropy.toFixed(4)}

Корреляция: ${t.correlation.toFixed(4)}

`; document.getElementById("image-meta-info").appendChild(texBlock); } graphChart.setOption({ tooltip: { show: false }, legend: [ { data: ['Изображение', 'Жанр', 'Стиль'], textStyle: { color: '#aaa' } } ], series: [{ type: 'graph', layout: 'force', roam: true, draggable: true, label: { show: true, formatter: '{b}', fontSize: 11, color: '#eee' }, edgeSymbol: ['circle', 'arrow'], edgeSymbolSize: [4, 6], force: { repulsion: 250, edgeLength: [80, 160] }, categories: [ { name: 'Изображение', itemStyle: { color: '#ffaa00' } }, { name: 'Жанр', itemStyle: { color: '#00ff88' } }, { name: 'Стиль', itemStyle: { color: '#00c4ff' } } ], data: fullNodes, links: fullLinks, lineStyle: { color: 'source', curveness: 0.2, opacity: 0.6 } }] }); graphChart.on('mouseover', params => { if (params.dataType === 'node' && params.data.name !== 'Изображение') { const { name, category, value } = params.data; const rect = document.getElementById('graph-container').getBoundingClientRect(); let description = ''; if (category === 1) { description = genreDescriptions[name] || 'Жанр: описание отсутствует.'; } else if (category === 2) { description = styleDescriptions[name] || 'Стиль: описание отсутствует.'; } tooltipEl.innerHTML = `
${name}
Уверенность: ${value.toFixed(2)}%
${description}
`; tooltipEl.style.left = `${rect.left + params.event.offsetX + 10}px`; tooltipEl.style.top = `${rect.top + params.event.offsetY - 20}px`; tooltipEl.style.display = 'block'; } }); graphChart.on('mouseout', () => { tooltipEl.style.display = 'none'; }); if (data.caption) { document.getElementById("caption-result").textContent = data.caption; } }) .catch(err => { genreResults.innerHTML = "Ошибка анализа."; styleResults.innerHTML = ""; console.error("Ошибка:", err); }) .finally(() => { loader.style.display = "none"; }); } window.addEventListener("load", () => { setTimeout(() => { const splash = document.getElementById("splash-screen"); if (splash) splash.style.display = "none"; }, 3000); }); });