Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| from transformers import pipeline | |
| import torch | |
| from PIL import Image | |
| import io | |
| import librosa | |
| import numpy as np | |
| import logging | |
| import tempfile | |
| import os | |
| from streamlit.runtime.uploaded_file_manager import UploadedFile | |
| from diffusers import StableDiffusionPipeline | |
| # Configurar página | |
| st.set_page_config( | |
| page_title="Aplicação de IA Multi-Modal", | |
| page_icon="🤖", | |
| layout="wide" | |
| ) | |
| # Configurar logging | |
| logging.basicConfig( | |
| filename='app_errors.log', | |
| level=logging.ERROR, | |
| format='%(asctime)s - %(levelname)s - %(message)s' | |
| ) | |
| # Cache para evitar recarregar modelos a cada execução | |
| def load_models(): | |
| """Carrega todos os modelos com cache para melhor performance""" | |
| device = torch.device("mps" if torch.backends.mps.is_available() else "cuda" if torch.cuda.is_available() else "cpu") | |
| logging.info(f"Usando dispositivo: {device}") | |
| models = {} | |
| try: | |
| # Modelos de texto | |
| models['sentiment_analysis'] = pipeline( | |
| "sentiment-analysis", | |
| model="cardiffnlp/twitter-roberta-base-sentiment-latest", | |
| device=device | |
| ) | |
| models['text_classification'] = pipeline( | |
| "text-classification", | |
| model="distilbert-base-uncased-finetuned-sst-2-english", | |
| device=device | |
| ) | |
| models['summarization'] = pipeline( | |
| "summarization", | |
| model="facebook/bart-large-cnn", | |
| device=device | |
| ) | |
| models['question_answering'] = pipeline( | |
| "question-answering", | |
| model="deepset/roberta-base-squad2", | |
| device=device | |
| ) | |
| models['translation'] = pipeline( | |
| "translation", | |
| model="Helsinki-NLP/opus-mt-tc-big-en-pt", | |
| device=device | |
| ) | |
| models['text_generation'] = pipeline( | |
| "text-generation", | |
| model="gpt2", | |
| device=device | |
| ) | |
| models['ner'] = pipeline( | |
| "ner", | |
| model="dbmdz/bert-large-cased-finetuned-conll03-english", | |
| device=device, | |
| aggregation_strategy="simple" | |
| ) | |
| # Modelos de imagem | |
| models['image_classification'] = pipeline( | |
| "image-classification", | |
| model="google/vit-base-patch16-224", | |
| device=device | |
| ) | |
| models['object_detection'] = pipeline( | |
| "object-detection", | |
| model="facebook/detr-resnet-50", | |
| device=device | |
| ) | |
| models['image_segmentation'] = pipeline( | |
| "image-segmentation", | |
| model="facebook/detr-resnet-50-panoptic", | |
| device=device | |
| ) | |
| models['facial_recognition'] = pipeline( | |
| "image-classification", | |
| model="mo-thecreator/vit-Facial-Expression-Recognition", | |
| device=device | |
| ) | |
| # Modelos de áudio | |
| models['speech_to_text'] = pipeline( | |
| "automatic-speech-recognition", | |
| model="openai/whisper-base", | |
| device=device | |
| ) | |
| models['audio_classification'] = pipeline( | |
| "audio-classification", | |
| model="superb/hubert-base-superb-er", | |
| device=device | |
| ) | |
| # Modelos generativos | |
| # --- Stable Diffusion com segurança dinâmica --- | |
| sd_dtype = torch.float16 if torch.cuda.is_available() else torch.float32 | |
| # Detecta se estamos em ambiente Hugging Face Spaces (produção pública) | |
| is_hf_spaces = "SPACE_ID" in os.environ or "HF_HOME" in os.environ | |
| if is_hf_spaces: | |
| # ✅ Ativa safety checker em produção | |
| safety_model_id = "runwayml/stable-diffusion-v1-5" | |
| safety_checker = StableDiffusionSafetyChecker.from_pretrained(safety_model_id) | |
| feature_extractor = AutoFeatureExtractor.from_pretrained(safety_model_id) | |
| models['text_to_image'] = StableDiffusionPipeline.from_pretrained( | |
| "runwayml/stable-diffusion-v1-5", | |
| torch_dtype=sd_dtype, | |
| use_safetensors=True, | |
| safety_checker=safety_checker, | |
| feature_extractor=feature_extractor | |
| ) | |
| else: | |
| # ⚡ Modo local: desativa safety checker por performance | |
| models['text_to_image'] = StableDiffusionPipeline.from_pretrained( | |
| "runwayml/stable-diffusion-v1-5", | |
| torch_dtype=sd_dtype, | |
| use_safetensors=True, | |
| safety_checker=None | |
| ) | |
| try: | |
| models['text_to_image'] = models['text_to_image'].to(device) | |
| models['text_to_image'].enable_attention_slicing() | |
| except Exception: | |
| logging.info("Otimizações de SD não aplicadas para este dispositivo.") | |
| except Exception as e: | |
| st.error(f"Erro crítico ao carregar modelos: {str(e)}") | |
| logging.exception("Erro ao carregar modelos") | |
| return {} | |
| return models | |
| def validate_audio_file(file: UploadedFile) -> bool: | |
| """Valida o arquivo de áudio""" | |
| valid_extensions = ['.wav', '.mp3', '.flac', '.m4a'] | |
| if not any(file.name.lower().endswith(ext) for ext in valid_extensions): | |
| return False | |
| return True | |
| def validate_image_file(file: UploadedFile) -> bool: | |
| """Valida o arquivo de imagem""" | |
| valid_extensions = ['.jpg', '.jpeg', '.png', '.bmp'] | |
| if not any(file.name.lower().endswith(ext) for ext in valid_extensions): | |
| return False | |
| try: | |
| Image.open(file).verify() | |
| # reposiciona o cursor para permitir leituras subsequentes | |
| try: | |
| file.seek(0) | |
| except Exception: | |
| pass | |
| return True | |
| except Exception: | |
| return False | |
| def process_audio_file(audio_file): | |
| """Processa arquivo de áudio para o formato correto""" | |
| try: | |
| # Criar arquivo temporário para processamento | |
| with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(audio_file.name)[1]) as tmp_file: | |
| tmp_file.write(audio_file.read()) | |
| tmp_file_path = tmp_file.name | |
| # Carregar áudio com librosa | |
| audio_array, sample_rate = librosa.load(tmp_file_path, sr=16000) | |
| os.unlink(tmp_file_path) | |
| return audio_array | |
| except Exception as e: | |
| st.error(f"Erro ao processar áudio: {str(e)}") | |
| logging.error(f"Erro no processamento de áudio: {e}") | |
| return None | |
| def process_image_file(image_file): | |
| """Processa arquivo de imagem""" | |
| try: | |
| image = Image.open(image_file) | |
| if image.mode != 'RGB': | |
| image = image.convert('RGB') | |
| return image | |
| except Exception as e: | |
| st.error(f"Erro ao processar imagem: {str(e)}") | |
| logging.error(f"Erro no processamento de imagem: {e}") | |
| return None | |
| def extract_images_from_sd_result(result): | |
| """Normaliza o retorno do StableDiffusionPipeline em uma lista de PIL Images.""" | |
| images = [] | |
| if hasattr(result, 'images'): | |
| images = result.images | |
| elif isinstance(result, list): | |
| images = result | |
| else: | |
| images = [result] | |
| normalized_images = [] | |
| for img in images: | |
| if isinstance(img, Image.Image): | |
| normalized_images.append(img) | |
| elif isinstance(img, np.ndarray): | |
| try: | |
| normalized_images.append(Image.fromarray(img)) | |
| except Exception: | |
| continue | |
| return normalized_images | |
| def display_results(result, model_key): | |
| """Exibe resultados formatados de acordo com o tipo de modelo""" | |
| if model_key == 'summarization': | |
| st.subheader("📝 Resumo Gerado") | |
| st.info(result[0]['summary_text']) | |
| elif model_key == 'translation': | |
| st.subheader("🌍 Tradução") | |
| st.success(result[0]['translation_text']) | |
| elif model_key in ['sentiment_analysis', 'text_classification']: | |
| st.subheader("📊 Resultados") | |
| for res in result: | |
| label = res['label'] | |
| score = res['score'] | |
| st.progress(float(score), text=f"{label} ({score:.2%})") | |
| elif model_key == 'ner': | |
| st.subheader("🔍 Entidades Reconhecidas") | |
| for entity in result: | |
| st.write(f"- **{entity['word']}**: {entity['entity_group']} (confiança: {entity['score']:.2%})") | |
| elif model_key == 'text_generation': | |
| st.subheader("🧠 Texto Gerado") | |
| st.write(result[0]['generated_text']) | |
| elif model_key == 'image_classification': | |
| st.subheader("🏷️ Classificação") | |
| for res in result[:5]: | |
| st.write(f"- **{res['label']}**: {res['score']:.2%}") | |
| elif model_key == 'object_detection': | |
| st.subheader("📦 Objetos Detectados") | |
| for obj in result: | |
| st.write(f"- {obj['label']} (confiança: {obj['score']:.2%})") | |
| elif model_key == 'image_segmentation': | |
| st.subheader("🧩 Segmentação") | |
| st.image(result[0]['mask'], caption="Máscara de segmentação", use_container_width=True) | |
| elif model_key == 'facial_recognition': | |
| st.subheader("😊 Reconhecimento Facial") | |
| top_result = result[0] | |
| st.write(f"**Emoção predominante**: {top_result['label']} (confiança: {top_result['score']:.2%})") | |
| elif model_key == 'speech_to_text': | |
| st.subheader("🔈 Transcrição") | |
| st.success(result['text']) | |
| elif model_key == 'audio_classification': | |
| st.subheader("🎧 Classificação de Áudio") | |
| top_emotion = result[0] | |
| st.write(f"**Emoção detectada**: {top_emotion['label']} (confiança: {top_emotion['score']:.2%})") | |
| elif model_key == 'text_to_image': | |
| st.subheader("🎨 Imagem Gerada") | |
| images = extract_images_from_sd_result(result) | |
| if not images: | |
| st.error("Nenhuma imagem foi gerada.") | |
| return | |
| if len(images) == 1: | |
| st.image(images[0], caption="Imagem gerada a partir do texto") | |
| else: | |
| st.image(images, caption=[f"Imagem {i+1}" for i in range(len(images))]) | |
| def main(): | |
| st.title("🤖 Aplicação de IA Multi-Modal Avançada") | |
| st.markdown("---") | |
| # Carregar modelos | |
| with st.spinner("Carregando modelos de IA... (Isso pode levar alguns minutos na primeira execução)"): | |
| models = load_models() | |
| if not models: | |
| st.error("Falha crítica ao carregar os modelos. Verifique os logs para mais detalhes.") | |
| return | |
| # Sidebar para seleção de modelo | |
| st.sidebar.title("⚙️ Configurações") | |
| model_categories = { | |
| "📝 Processamento de Texto": [ | |
| ("Análise de Sentimento", "sentiment_analysis"), | |
| ("Classificação de Texto", "text_classification"), | |
| ("Resumo de Texto", "summarization"), | |
| ("Perguntas e Respostas", "question_answering"), | |
| ("Tradução (EN→PT)", "translation"), | |
| ("Reconhecimento de Entidades", "ner"), | |
| ("Geração de Texto", "text_generation") | |
| ], | |
| "🖼️ Processamento de Imagem": [ | |
| ("Classificação de Imagem", "image_classification"), | |
| ("Detecção de Objetos", "object_detection"), | |
| ("Segmentação de Imagem", "image_segmentation"), | |
| ("Reconhecimento Facial", "facial_recognition") | |
| ], | |
| "🎵 Processamento de Áudio": [ | |
| ("Transcrição de Áudio", "speech_to_text"), | |
| ("Classificação de Emoções", "audio_classification") | |
| ], | |
| "✨ Modelos Generativos": [ | |
| ("Texto para Imagem", "text_to_image") | |
| ] | |
| } | |
| selected_category = st.sidebar.selectbox( | |
| "Categoria", | |
| list(model_categories.keys()), | |
| index=0 | |
| ) | |
| selected_model = st.sidebar.selectbox( | |
| "Modelo", | |
| [name for name, key in model_categories[selected_category]], | |
| format_func=lambda x: x, | |
| index=0 | |
| ) | |
| # Obter chave do modelo selecionado | |
| model_key = next(key for name, key in model_categories[selected_category] if name == selected_model) | |
| # Interface principal | |
| st.header(f"{selected_model}") | |
| # Accordion para informações do modelo | |
| with st.expander("ℹ️ Sobre este modelo"): | |
| model_info = { | |
| 'sentiment_analysis': "Analisa o sentimento expresso em um texto (positivo/negativo/neutro)", | |
| 'text_classification': "Classifica textos em categorias pré-definidas", | |
| 'summarization': "Gera um resumo conciso de um texto longo", | |
| 'question_answering': "Responde perguntas baseadas em um contexto fornecido", | |
| 'translation': "Traduz texto de inglês para português", | |
| 'ner': "Identifica e classifica entidades nomeadas (pessoas, lugares, organizações)", | |
| 'text_generation': "Gera texto criativo continuando a partir de um prompt", | |
| 'image_classification': "Identifica objetos e cenas em imagens", | |
| 'object_detection': "Detecta e localiza múltiplos objetos em uma imagem", | |
| 'image_segmentation': "Segmenta diferentes elementos em uma imagem", | |
| 'facial_recognition': "Reconhece características faciais e emoções", | |
| 'speech_to_text': "Transcreve fala em texto", | |
| 'audio_classification': "Classifica emoções em arquivos de áudio", | |
| 'text_to_image': "Gera imagens a partir de descrições textuais" | |
| } | |
| st.info(model_info.get(model_key, "Informações detalhadas sobre este modelo.")) | |
| # Processamento baseado no tipo de modelo | |
| try: | |
| if model_key in ['sentiment_analysis', 'text_classification', 'summarization', | |
| 'translation', 'text_generation', 'ner']: | |
| handle_text_models(models, model_key, selected_model) | |
| elif model_key == 'question_answering': | |
| handle_qa_model(models, model_key) | |
| elif model_key in ['image_classification', 'object_detection', | |
| 'image_segmentation', 'facial_recognition']: | |
| handle_image_models(models, model_key, selected_model) | |
| elif model_key in ['speech_to_text', 'audio_classification']: | |
| handle_audio_models(models, model_key) | |
| elif model_key == 'text_to_image': | |
| handle_generative_models(models, model_key) | |
| except Exception as e: | |
| st.error(f"Erro inesperado durante a execução: {str(e)}") | |
| logging.exception("Erro durante a execução do modelo") | |
| def handle_text_models(models, model_key, model_name): | |
| """Manipula modelos de texto""" | |
| input_text = st.text_area( | |
| f"Digite o texto para {model_name.lower()}:", | |
| height=200, | |
| placeholder="Cole ou digite seu texto aqui...", | |
| key=f"text_input_{model_key}" | |
| ) | |
| # Parâmetros adicionais para alguns modelos | |
| advanced_params = {} | |
| if model_key == 'summarization': | |
| with st.expander("⚙️ Parâmetros Avançados"): | |
| advanced_params['max_length'] = st.slider("Comprimento máximo", 50, 300, 150) | |
| advanced_params['min_length'] = st.slider("Comprimento mínimo", 10, 100, 30) | |
| if model_key == 'text_generation': | |
| with st.expander("⚙️ Parâmetros Avançados"): | |
| advanced_params['max_length'] = st.slider("Comprimento do texto", 50, 500, 100) | |
| advanced_params['temperature'] = st.slider("Criatividade", 0.1, 1.0, 0.7) | |
| advanced_params['num_return_sequences'] = st.slider("Número de resultados", 1, 5, 1) | |
| if st.button(f"🚀 Executar {model_name}", type="primary", key=f"btn_{model_key}"): | |
| if input_text.strip(): | |
| with st.spinner("Processando..."): | |
| try: | |
| if model_key == 'ner': | |
| result = models[model_key](input_text) | |
| else: | |
| result = models[model_key](input_text, **advanced_params) | |
| display_results(result, model_key) | |
| except Exception as e: | |
| st.error(f"Erro ao processar texto: {str(e)}") | |
| logging.error(f"Erro no modelo {model_key}: {e}") | |
| else: | |
| st.warning("⚠️ Por favor, insira um texto válido.") | |
| def handle_qa_model(models, model_key): | |
| """Manipula modelo de Q&A""" | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| context = st.text_area( | |
| "Contexto:", | |
| height=200, | |
| placeholder="Cole o texto que contém a informação...", | |
| key="qa_context" | |
| ) | |
| with col2: | |
| question = st.text_area( | |
| "Pergunta:", | |
| height=150, | |
| placeholder="Faça sua pergunta sobre o contexto...", | |
| key="qa_question" | |
| ) | |
| with st.expander("⚙️ Parâmetros Avançados"): | |
| confidence_threshold = st.slider("Limite de confiança", 0.0, 1.0, 0.5, 0.01) | |
| if st.button("🚀 Executar Pergunta e Resposta", type="primary", key="btn_qa"): | |
| if context.strip() and question.strip(): | |
| with st.spinner("Buscando resposta..."): | |
| try: | |
| result = models[model_key](question=question, context=context) | |
| if result['score'] < confidence_threshold: | |
| st.warning(f"⚠️ Confiança baixa na resposta ({result['score']:.2%})") | |
| st.success("🔍 Resposta encontrada:") | |
| st.markdown(f"**Resposta:** {result['answer']}") | |
| st.markdown(f"**Confiança:** {result['score']:.2%}") | |
| except Exception as e: | |
| st.error(f"Erro ao processar Q&A: {str(e)}") | |
| logging.error(f"Erro no modelo Q&A: {e}") | |
| else: | |
| st.warning("⚠️ Por favor, forneça tanto o contexto quanto a pergunta.") | |
| def handle_image_models(models, model_key, model_name): | |
| """Manipula modelos de imagem""" | |
| uploaded_file = st.file_uploader( | |
| "Carregue uma imagem", | |
| type=["jpg", "png", "jpeg", "bmp"], | |
| help="Formatos suportados: JPG, PNG, JPEG, BMP", | |
| key=f"img_upload_{model_key}" | |
| ) | |
| if uploaded_file is not None: | |
| if not validate_image_file(uploaded_file): | |
| st.error("⚠️ Formato de arquivo inválido ou arquivo corrompido.") | |
| return | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.subheader("🖼️ Imagem Original") | |
| image = process_image_file(uploaded_file) | |
| if image: | |
| st.image(image, use_container_width=True) | |
| with col2: | |
| st.subheader("📊 Resultados") | |
| if st.button(f"🚀 Executar {model_name}", type="primary", key=f"btn_img_{model_key}"): | |
| if image: | |
| with st.spinner("Analisando imagem..."): | |
| try: | |
| result = models[model_key](image) | |
| display_results(result, model_key) | |
| except Exception as e: | |
| st.error(f"Erro ao processar imagem: {str(e)}") | |
| logging.error(f"Erro no modelo {model_key}: {e}") | |
| def handle_audio_models(models, model_key): | |
| """Manipula modelos de áudio""" | |
| model_name = "Transcrição de Áudio" if model_key == 'speech_to_text' else "Classificação de Áudio" | |
| uploaded_file = st.file_uploader( | |
| f"Carregue um arquivo de áudio para {model_name}", | |
| type=["wav", "mp3", "flac", "m4a"], | |
| help="Formatos suportados: WAV, MP3, FLAC, M4A", | |
| key=f"audio_upload_{model_key}" | |
| ) | |
| if uploaded_file is not None: | |
| if not validate_audio_file(uploaded_file): | |
| st.error("⚠️ Formato de arquivo inválido ou não suportado.") | |
| return | |
| st.audio(uploaded_file) | |
| if st.button(f"🚀 Executar {model_name}", type="primary", key=f"btn_audio_{model_key}"): | |
| with st.spinner("Processando áudio..."): | |
| try: | |
| audio_array = process_audio_file(uploaded_file) | |
| if audio_array is not None: | |
| result = models[model_key]({"array": audio_array, "sampling_rate": 16000}) | |
| display_results(result, model_key) | |
| else: | |
| st.error("Não foi possível processar o arquivo de áudio.") | |
| except Exception as e: | |
| st.error(f"Erro ao processar áudio: {str(e)}") | |
| logging.error(f"Erro no modelo {model_key}: {e}") | |
| def handle_generative_models(models, model_key): | |
| """Manipula modelos generativos""" | |
| prompt = st.text_area( | |
| "Descrição da imagem:", | |
| height=150, | |
| placeholder="Descreva a imagem que deseja gerar...", | |
| key="text_to_image_prompt" | |
| ) | |
| with st.expander("⚙️ Parâmetros Avançados"): | |
| cols = st.columns(2) | |
| with cols[0]: | |
| width = st.slider("Largura", 256, 1024, 512, 64) | |
| with cols[1]: | |
| height = st.slider("Altura", 256, 1024, 512, 64) | |
| num_images = st.slider("Número de imagens", 1, 4, 1) | |
| guidance_scale = st.slider("Escala de orientação", 1.0, 20.0, 7.5) | |
| if st.button("🚀 Gerar Imagem", type="primary", key="btn_text_to_image"): | |
| if prompt.strip(): | |
| with st.spinner("Criando imagem..."): | |
| try: | |
| result = models[model_key]( | |
| prompt, | |
| height=height, | |
| width=width, | |
| num_images_per_prompt=num_images, | |
| guidance_scale=guidance_scale | |
| ) | |
| display_results(result, model_key) | |
| except Exception as e: | |
| st.error(f"Erro ao gerar imagem: {str(e)}") | |
| logging.error(f"Erro no modelo text-to-image: {e}") | |
| else: | |
| st.warning("⚠️ Por favor, insira uma descrição para a imagem.") | |
| if __name__ == "__main__": | |
| main() |