Spaces:
Running
on
Zero
Running
on
Zero
import gradio as gr | |
import spaces | |
import torch | |
from transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessor | |
import os | |
from datetime import datetime | |
from PIL import Image | |
import requests | |
from io import BytesIO | |
# Model setup | |
processor = AutoProcessor.from_pretrained("deepguess/weather-vlm-qwen2.5-7b", trust_remote_code=True) | |
model = Qwen2_5_VLForConditionalGeneration.from_pretrained( | |
"deepguess/weather-vlm-qwen2.5-7b", | |
torch_dtype=torch.float16, | |
trust_remote_code=True | |
) | |
model.eval() | |
# Title and description | |
TITLE = "🌦️ Weather Analysis VLM (Qwen2.5-VL-7B Fine-tuned)" | |
DESCRIPTION = """ | |
## Advanced Weather Image Analysis | |
This model specializes in analyzing weather data including: | |
- **Model Outputs**: GFS, HRRR, ECMWF, NAM analysis | |
- **Soundings**: Skew-T diagrams, hodographs, SHARPpy analysis | |
- **Observations**: Surface obs, satellite, radar imagery | |
- **Forecasts**: Deterministic and ensemble model outputs | |
- **Severe Weather**: Convective parameters, SPC outlooks | |
### ⚠️ Disclaimer | |
**For educational and research purposes only. Not for operational forecasting.** | |
""" | |
# Enhanced prompts based on your data categories | |
PROMPT_TEMPLATES = { | |
"Quick Analysis": "Describe the weather in this image.", | |
"Model Output": "Analyze this model output. What patterns and features are shown?", | |
"Sounding Analysis": "Analyze this sounding. Discuss stability, shear, and severe potential.", | |
"Radar/Satellite": "Describe the features in this radar or satellite image.", | |
"Severe Weather": "Assess severe weather potential based on this image.", | |
"Technical Deep Dive": "Provide detailed technical analysis including parameters and meteorological significance.", | |
"Forecast Discussion": "Based on this image, what weather evolution is expected?", | |
"Pattern Recognition": "Identify synoptic patterns, jet streaks, troughs, ridges, and fronts.", | |
"Ensemble Analysis": "Analyze ensemble spread, uncertainty, and most likely scenarios.", | |
"Winter Weather": "Analyze precipitation type, accumulation potential, and impacts.", | |
} | |
# System prompts for different analysis modes | |
SYSTEM_PROMPTS = { | |
"technical": """You are an expert meteorologist providing technical analysis. Focus on: | |
- Specific parameter values and thresholds | |
- Physical processes and dynamics | |
- Pattern recognition and anomalies | |
- Forecast confidence and uncertainty | |
Use technical terminology appropriately.""", | |
"educational": """You are a meteorology instructor. Explain concepts clearly while maintaining accuracy. | |
Point out key features and explain their significance. Use some technical terms but define them.""", | |
"operational": """You are providing a weather briefing. Focus on: | |
- Current conditions and trends | |
- Expected evolution | |
- Impacts and hazards | |
- Timing of changes | |
Be concise but thorough.""", | |
"research": """You are analyzing meteorological data for research purposes. Discuss: | |
- Interesting features or anomalies | |
- Comparison to climatology | |
- Physical mechanisms | |
- Uncertainty quantification""" | |
} | |
# Analysis mode descriptions | |
MODE_INFO = { | |
"technical": "Detailed technical analysis for meteorologists", | |
"educational": "Clear explanations for learning", | |
"operational": "Focused briefing style", | |
"research": "In-depth research perspective" | |
} | |
# Example URLs for different weather data types | |
EXAMPLE_URLS = { | |
"SPC Convective Outlook": "https://www.spc.noaa.gov/products/outlook/day1otlk.gif", | |
"SPC Mesoanalysis (Surface)": "https://inside.nssl.noaa.gov/ewp/wp-content/uploads/sites/22/2019/05/19ZSPCCape.png", | |
"College of DuPage Nexrad": "https://upload.wikimedia.org/wikipedia/commons/9/9b/NEXRAD_radar_of_an_EF2_tornado_in_Kansas_on_March_13%2C_2024.png", | |
"Tropical Tidbits GFS 500mb": "https://sites.gatech.edu/eas-mesoscale-blog/files/2023/04/Hebert_Fig4-768x531.png", | |
"GOES-16 Visible": "https://cdn.star.nesdis.noaa.gov/GOES16/ABI/CONUS/GEOCOLOR/1250x750.jpg", | |
"KOUN Sounding": "https://sharppy.github.io/SHARPpy/_images/gui.sharppy.png", | |
} | |
def load_image_from_url(url): | |
"""Load an image from URL.""" | |
try: | |
response = requests.get(url, timeout=10, headers={'User-Agent': 'Mozilla/5.0'}) | |
response.raise_for_status() | |
img = Image.open(BytesIO(response.content)) | |
# Convert to RGB if necessary | |
if img.mode != 'RGB': | |
img = img.convert('RGB') | |
return img | |
except Exception as e: | |
return None, f"Error loading image from URL: {str(e)}" | |
def analyze_weather_image(image, image_url, analysis_type, custom_prompt, analysis_mode, temperature, max_tokens, top_p): | |
# Handle image input - either direct upload or URL | |
if image_url and image_url.strip(): | |
result = load_image_from_url(image_url.strip()) | |
if isinstance(result, tuple): # Error case | |
return result[1] | |
image = result | |
elif image is None: | |
return "Please upload an image or provide an image URL to analyze." | |
# Move model to GPU | |
model.cuda() | |
# Use custom prompt if provided, otherwise use template | |
prompt = custom_prompt.strip() if custom_prompt.strip() else PROMPT_TEMPLATES.get(analysis_type, PROMPT_TEMPLATES["Quick Analysis"]) | |
# Select system prompt based on mode | |
system_content = SYSTEM_PROMPTS.get(analysis_mode, SYSTEM_PROMPTS["technical"]) | |
# Prepare messages | |
messages = [{ | |
"role": "system", | |
"content": system_content | |
}, { | |
"role": "user", | |
"content": [ | |
{"type": "text", "text": prompt}, | |
{"type": "image", "image": image} | |
] | |
}] | |
# Process inputs | |
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) | |
inputs = processor(text=[text], images=[image], return_tensors="pt").to("cuda") | |
# Generate with specified parameters | |
outputs = model.generate( | |
**inputs, | |
max_new_tokens=max_tokens, | |
temperature=temperature, | |
do_sample=True, | |
top_p=top_p, | |
repetition_penalty=1.05 | |
) | |
# Decode response | |
response = processor.batch_decode(outputs[:, inputs.input_ids.shape[1]:], skip_special_tokens=True)[0] | |
# Add metadata | |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC") | |
source = "URL" if image_url and image_url.strip() else "Upload" | |
metadata = f"\n\n---\n*Analysis completed: {timestamp} | Mode: {analysis_mode} | Type: {analysis_type} | Source: {source}*" | |
return response + metadata | |
# Create Gradio interface | |
with gr.Blocks(title=TITLE, theme=gr.themes.Base(), css=""" | |
.gradio-container { font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; } | |
.markdown-text { font-size: 14px; } | |
#analysis-output { font-family: 'Monaco', 'Menlo', monospace; font-size: 13px; } | |
.gr-button-primary { background-color: #2563eb; } | |
.gr-button-primary:hover { background-color: #1e40af; } | |
.url-button { min-width: 120px; } | |
""") as demo: | |
gr.Markdown(f"# {TITLE}") | |
gr.Markdown(DESCRIPTION) | |
with gr.Row(): | |
with gr.Column(scale=1): | |
# Image input options | |
with gr.Tabs(): | |
with gr.Tab("Upload Image"): | |
image_input = gr.Image( | |
label="Upload Weather Image", | |
type="pil", | |
elem_id="image-upload" | |
) | |
with gr.Tab("Image URL"): | |
image_url_input = gr.Textbox( | |
label="Image URL", | |
placeholder="https://example.com/weather-image.jpg", | |
lines=1 | |
) | |
# Quick URL examples | |
gr.Markdown("**Quick Examples:**") | |
url_buttons = [] | |
for name, url in EXAMPLE_URLS.items(): | |
btn = gr.Button(name, size="sm", elem_classes="url-button") | |
btn.click(lambda u=url: u, outputs=image_url_input) | |
# Analysis type selector | |
analysis_type = gr.Dropdown( | |
label="Analysis Type", | |
choices=list(PROMPT_TEMPLATES.keys()), | |
value="Quick Analysis", | |
info="Select the type of analysis you need" | |
) | |
# Analysis mode selector | |
analysis_mode = gr.Radio( | |
label="Analysis Mode", | |
choices=list(MODE_INFO.keys()), | |
value="technical", | |
info="Choose the style and depth of analysis" | |
) | |
# Mode description | |
mode_description = gr.Markdown(value=MODE_INFO["technical"], elem_id="mode-desc") | |
# Custom prompt option | |
with gr.Accordion("Custom Prompt (Optional)", open=False): | |
custom_prompt = gr.Textbox( | |
label="Enter your specific question or analysis request", | |
placeholder="E.g., 'Focus on the 500mb vorticity patterns' or 'Explain the hodograph curvature'", | |
lines=3 | |
) | |
# Advanced settings | |
with gr.Accordion("Advanced Settings", open=False): | |
with gr.Row(): | |
temperature = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
value=0.7, | |
step=0.05, | |
label="Temperature", | |
info="Lower = more focused, Higher = more varied" | |
) | |
top_p = gr.Slider( | |
minimum=0.5, | |
maximum=1.0, | |
value=0.95, | |
step=0.05, | |
label="Top-p", | |
info="Nucleus sampling threshold" | |
) | |
max_tokens = gr.Slider( | |
minimum=128, | |
maximum=1024, | |
value=512, | |
step=64, | |
label="Max Output Length", | |
info="Longer for detailed analysis" | |
) | |
# Analyze button | |
analyze_btn = gr.Button("🔍 Analyze Weather", variant="primary", size="lg") | |
with gr.Column(scale=1): | |
# Output area | |
output = gr.Textbox( | |
label="Analysis Results", | |
lines=25, | |
max_lines=30, | |
show_copy_button=True, | |
elem_id="analysis-output" | |
) | |
# Common weather data categories for quick access | |
with gr.Accordion("📊 Quick Templates for Common Data Types", open=False): | |
gr.Markdown(""" | |
### Click to load analysis templates: | |
""") | |
with gr.Row(): | |
gr.Button("500mb Analysis", size="sm").click( | |
lambda: "Analyze the 500mb height and wind patterns. Identify troughs, ridges, jet streaks, and vorticity.", | |
outputs=custom_prompt | |
) | |
gr.Button("Sounding Analysis", size="sm").click( | |
lambda: "Analyze this sounding for stability, CAPE, shear, LCL, LFC, and severe weather parameters.", | |
outputs=custom_prompt | |
) | |
gr.Button("Composite Reflectivity", size="sm").click( | |
lambda: "Analyze radar reflectivity patterns, storm structure, intensity, and movement.", | |
outputs=custom_prompt | |
) | |
with gr.Row(): | |
gr.Button("Surface Analysis", size="sm").click( | |
lambda: "Analyze surface features including fronts, pressure centers, convergence, and boundaries.", | |
outputs=custom_prompt | |
) | |
gr.Button("Ensemble Spread", size="sm").click( | |
lambda: "Analyze ensemble spread, clustering, and probabilistic information.", | |
outputs=custom_prompt | |
) | |
gr.Button("Convective Parameters", size="sm").click( | |
lambda: "Analyze CAPE, CIN, SRH, bulk shear, and composite parameters for severe potential.", | |
outputs=custom_prompt | |
) | |
# Tips section | |
with gr.Accordion("💡 Pro Tips for Best Results", open=False): | |
gr.Markdown(""" | |
### Image Guidelines: | |
- **Resolution**: Higher resolution images yield better analysis | |
- **Clarity**: Ensure text/contours are legible | |
- **Completeness**: Include colorbars, titles, valid times | |
### Using URLs: | |
- Supports direct links to JPG, PNG, GIF images | |
- Some weather sites may block direct access | |
- For best results, use official weather service URLs | |
- Alternative: Save image locally and upload | |
### Common Weather Data Sources: | |
- **SPC**: Convective outlooks, mesoanalysis | |
- **College of DuPage**: NEXRAD, model data | |
- **Tropical Tidbits**: Model analysis maps | |
- **GOES Imagery**: Satellite data | |
- **WPC**: Surface analysis, QPF | |
### Analysis Tips by Data Type: | |
**Model Output (GFS, HRRR, ECMWF, NAM):** | |
- Include initialization and valid times | |
- Specify if you want focus on particular features | |
- Ask about ensemble uncertainty if applicable | |
**Soundings (Skew-T, Hodographs):** | |
- Ensure all parameters are visible | |
- Ask about specific levels or layers | |
- Request shear calculations or thermodynamic analysis | |
**Radar/Satellite:** | |
- Include timestamp and location | |
- Specify interest in particular features | |
- Ask about storm motion or development | |
""") | |
# Update mode description when mode changes | |
analysis_mode.change( | |
lambda mode: MODE_INFO[mode], | |
inputs=analysis_mode, | |
outputs=mode_description | |
) | |
# Set up event handler | |
analyze_btn.click( | |
fn=analyze_weather_image, | |
inputs=[image_input, image_url_input, analysis_type, custom_prompt, analysis_mode, temperature, max_tokens, top_p], | |
outputs=output | |
) | |
# Clear image upload when URL is entered | |
image_url_input.change( | |
lambda: None, | |
outputs=image_input | |
) | |
# Launch the app | |
if __name__ == "__main__": | |
demo.launch() |