feat: Init
This commit is contained in:
+53
@@ -0,0 +1,53 @@
|
||||
import io
|
||||
import os
|
||||
from fastapi import FastAPI, UploadFile, File, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from PIL import Image
|
||||
|
||||
# Импортируем твои существующие загрузчики (они теперь работают только на бэкенде)
|
||||
from data_loader import load_music_engine, load_image_processor
|
||||
|
||||
app = FastAPI(title="EmoM Inference API", version="1.0.0")
|
||||
|
||||
# Глобальный кэш для удержания моделей в памяти
|
||||
ml_context = {
|
||||
"image_processor": None,
|
||||
"music_matcher": None
|
||||
}
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
print("Инициализация нейросетевого ядра EmoM...")
|
||||
ml_context["image_processor"] = load_image_processor()
|
||||
ml_context["music_matcher"] = load_music_engine()
|
||||
|
||||
if not ml_context["image_processor"] or not ml_context["music_matcher"]:
|
||||
raise RuntimeError("Отказ системы: Артефакты моделей не найдены.")
|
||||
print("Вычислительный конвейер готов к работе.")
|
||||
|
||||
@app.post("/analyze")
|
||||
async def analyze_image_endpoint(file: UploadFile = File(...)):
|
||||
"""
|
||||
Принимает изображение, прогоняет через ResNet и возвращает треки из DEAM.
|
||||
"""
|
||||
try:
|
||||
# 1. Чтение бинарных данных из запроса
|
||||
image_bytes = await file.read()
|
||||
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
|
||||
|
||||
# 2. Инференс (ВНИМАНИЕ: здесь используй реальные названия методов из своих классов!)
|
||||
# Предположим, твой процессор выдает координаты V/A
|
||||
v_a_coords = ml_context["image_processor"].extract_va(image)
|
||||
|
||||
# 3. Поиск треков в базе
|
||||
matched_tracks = ml_context["music_matcher"].find_tracks(v_a_coords)
|
||||
|
||||
# 4. Формирование ответа
|
||||
return JSONResponse(content={
|
||||
"status": "success",
|
||||
"valence_arousal": v_a_coords,
|
||||
"tracks": matched_tracks
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Ошибка инференса: {str(e)}")
|
||||
+45
-56
@@ -1,73 +1,62 @@
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import requests
|
||||
import streamlit as st
|
||||
import streamlit.components.v1 as components
|
||||
|
||||
from data_loader import load_music_engine, load_emoset_data, load_image_processor
|
||||
from tabs.tab_dataset import render_dataset_tab
|
||||
from tabs.tab_live import render_live_tab
|
||||
|
||||
# Костыль для прямого запуска
|
||||
if __name__ == "__main__":
|
||||
if "STREAMLIT_RUN" not in os.environ:
|
||||
os.environ["STREAMLIT_RUN"] = "1"
|
||||
cmd = [sys.executable, "-m", "streamlit", "run", __file__, "--server.port", "8080", "--server.address", "0.0.0.0"]
|
||||
subprocess.run(cmd)
|
||||
sys.exit()
|
||||
|
||||
viewport_mode = st.query_params.get("viewport", "desktop")
|
||||
page_layout = "centered" if viewport_mode == "mobile" else "wide"
|
||||
|
||||
st.set_page_config(page_title="Thesis Demo", layout=page_layout)
|
||||
|
||||
# Определения ширины экрана и смены верстки
|
||||
components.html(
|
||||
"""
|
||||
<script>
|
||||
const w = window.parent.innerWidth;
|
||||
const h = window.parent.innerHeight;
|
||||
const url = new URL(window.parent.location.href);
|
||||
|
||||
// Считаем мобилкой, если ушли в портретный режим или экран уже 768px
|
||||
const isMobile = (h > w) || (w < 768);
|
||||
const target = isMobile ? "mobile" : "desktop";
|
||||
|
||||
if (url.searchParams.get("viewport") !== target) {
|
||||
url.searchParams.set("viewport", target);
|
||||
window.parent.location.href = url.href;
|
||||
}
|
||||
</script>
|
||||
""",
|
||||
height=0,
|
||||
width=0,
|
||||
# Конфигурация UI
|
||||
st.set_page_config(
|
||||
page_title="EmoM | EmotionMusic",
|
||||
layout="wide",
|
||||
initial_sidebar_state="collapsed"
|
||||
)
|
||||
|
||||
st.markdown(
|
||||
"""
|
||||
<style>
|
||||
img { max-width: 100%; height: auto; object-fit: contain; }
|
||||
[data-testid="stMetricValue"] { font-size: 1.8rem; }
|
||||
img { max-width: 100%; height: auto; object-fit: contain; border-radius: 4px; }
|
||||
[data-testid="stMetricValue"] { font-size: 1.8rem; font-weight: 600; }
|
||||
#MainMenu {visibility: hidden;}
|
||||
footer {visibility: hidden;}
|
||||
</style>
|
||||
""",
|
||||
unsafe_allow_html=True
|
||||
)
|
||||
|
||||
# Подгрузка ML-моделей и датасета
|
||||
music_matcher = load_music_engine()
|
||||
img_processor = load_image_processor()
|
||||
emoset_files, emoset_embeddings, emoset_labels, emoset_path = load_emoset_data()
|
||||
# Маршрутизация к нашему новому микросервису (берется из .env, либо локалхост)
|
||||
API_URL = os.getenv("BACKEND_API_URL", "http://localhost:8000") + "/analyze"
|
||||
|
||||
st.title("Генератор саундтреков (Research Demo)")
|
||||
def main():
|
||||
st.title("Система генерации саундтреков (EmoM)")
|
||||
st.caption("Микросервисная архитектура: Frontend (Streamlit) -> REST API -> PyTorch/DEAM")
|
||||
|
||||
tab_live, tab_debug = st.tabs(["Анализ событий (Свои фото)", "Отладка (Датасет EmoSet)"])
|
||||
uploaded_file = st.file_uploader("Загрузите изображение для анализа", type=["jpg", "jpeg", "png"])
|
||||
|
||||
with tab_live:
|
||||
if img_processor:
|
||||
render_live_tab(music_matcher, img_processor)
|
||||
else:
|
||||
st.error("Ошибка загрузки: не найдены веса ResNet для image_processor.")
|
||||
if uploaded_file is not None:
|
||||
st.image(uploaded_file, caption="Входной визуальный контент")
|
||||
|
||||
if st.button("Анализировать"):
|
||||
with st.spinner("Отправка данных в вычислительный кластер..."):
|
||||
try:
|
||||
# Отправляем POST-запрос в наш FastAPI микросервис
|
||||
files = {"file": (uploaded_file.name, uploaded_file.getvalue(), uploaded_file.type)}
|
||||
response = requests.post(API_URL, files=files, timeout=30)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
st.success("Анализ успешно завершен!")
|
||||
|
||||
# Вывод результатов
|
||||
st.subheader("Результаты анализа")
|
||||
st.write(f"Координаты Valence/Arousal: {data.get('valence_arousal')}")
|
||||
st.write("Подобранные треки:")
|
||||
st.json(data.get('tracks'))
|
||||
|
||||
# Здесь в будущем можно добавить обращение к Ollama для генерации красивого описания
|
||||
|
||||
else:
|
||||
st.error(f"Ошибка сервера: {response.text}")
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
st.error("Ошибка сети: Микросервис инференса недоступен. Проверьте статус Docker-контейнера emom_inference.")
|
||||
|
||||
with tab_debug:
|
||||
render_dataset_tab(music_matcher, emoset_files, emoset_embeddings, emoset_labels, emoset_path)
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user