Spaces:
Sleeping
Sleeping
File size: 8,521 Bytes
4d15178 7078f15 4d15178 82ce6d4 7078f15 82ce6d4 4d15178 7078f15 82ce6d4 7078f15 82ce6d4 4d15178 5cf0296 4d15178 5cf0296 4d15178 82ce6d4 5cf0296 4d15178 5cf0296 4d15178 82ce6d4 7078f15 5cf0296 82ce6d4 7078f15 82ce6d4 7078f15 82ce6d4 4d15178 7078f15 4d15178 5cf0296 82ce6d4 4d15178 7078f15 4d15178 7078f15 4d15178 c7be050 4d15178 7078f15 4d15178 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | import streamlit as st
import pandas as pd
import os
# ==========================================
# 1. PAGE CONFIGURATION
# ==========================================
st.set_page_config(page_title="Horror Reference Library", layout="wide")
st.title("π½οΈ Horror Reference Library")
st.markdown("### Search 11,500+ Cinematic AI-Tagged Comic Panels")
# ==========================================
# 2. DATA BUCKETING & CLEANING
# ==========================================
# Camera, Mood, and Lighting Buckets
def categorize_camera(text):
text = str(text).lower()
if 'dutch' in text: return 'Dutch Angle'
elif 'extreme close' in text or 'ecu' in text: return 'Extreme Close Up'
elif 'close' in text or 'cu' in text: return 'Close Up'
elif 'wide' in text or 'long' in text or 'establishing' in text: return 'Wide Shot'
elif 'mid' in text or 'medium' in text: return 'Mid Shot'
elif 'low angle' in text or 'looking up' in text: return 'Low Angle'
elif 'high angle' in text or 'looking down' in text: return 'High Angle'
elif 'pov' in text or 'point of view' in text: return 'Point of View'
else: return 'Other / Mixed'
def categorize_mood(text):
text = str(text).lower()
if 'tense' in text or 'suspense' in text or 'anxiety' in text: return 'Tense & Suspenseful'
elif 'action' in text or 'chaos' in text or 'dynamic' in text: return 'Action & Chaos'
elif 'creepy' in text or 'eerie' in text or 'ominous' in text: return 'Creepy & Eerie'
elif 'gore' in text or 'violent' in text or 'blood' in text: return 'Gore & Violence'
elif 'sad' in text or 'melancholy' in text or 'somber' in text: return 'Somber & Melancholic'
else: return 'Neutral / Standard'
def categorize_lighting(text):
text = str(text).lower()
if 'silhouette' in text: return 'Silhouetted'
elif 'high contrast' in text or 'chiaroscuro' in text: return 'High Contrast'
elif 'low key' in text or 'shadow' in text or 'dark' in text: return 'Low Key (Shadowy)'
elif 'harsh' in text or 'bright' in text: return 'Harsh & Bright'
elif 'flat' in text or 'even' in text: return 'Flat Lighting'
else: return 'Standard Lighting'
# NEW: Location, Subjects, and Action Buckets
def categorize_location(row):
text = str(row.get('location_setup', '')).lower() + " " + str(row.get('description', '')).lower()
if any(w in text for w in ['indoor', 'interior', 'room', 'house', 'building', 'office', 'corridor', 'hallway', 'wall', 'window', 'door', 'basement', 'stairs']): return 'Indoor'
if any(w in text for w in ['outdoor', 'exterior', 'street', 'sky', 'forest', 'mountain', 'landscape', 'city', 'outside', 'woods', 'road', 'night', 'moon', 'ocean']): return 'Outdoor'
return 'Unspecified / Mixed'
def categorize_subject(row):
text = str(row.get('staging', '')).lower() + " " + str(row.get('description', '')).lower()
if any(w in text for w in ['group', 'crowd', 'three', 'four', 'multiple', 'several', 'guests', 'army', 'mob', 'people']): return 'Group (3+ People)'
if any(w in text for w in ['two', 'couple', 'duo', 'both', 'pair']): return 'Two Characters'
if any(w in text for w in ['man', 'woman', 'boy', 'girl', 'figure', 'character', 'person', 'creature', 'monster']): return 'Single Subject'
return 'Object / Environment'
def categorize_action(row):
text = str(row.get('staging', '')).lower() + " " + str(row.get('description', '')).lower()
if any(w in text for w in ['action', 'fight', 'strike', 'combat', 'running', 'chasing', 'attack', 'lunging', 'falling', 'fleeing', 'struggle', 'violence', 'grab']): return 'Action Sequence'
if any(w in text for w in ['dialogue', 'talking', 'discussing', 'speaking', 'speech', 'conversation', 'yelling', 'screaming', 'whispering', 'saying']): return 'Dialogue / Conversation'
if any(w in text for w in ['reacts', 'reaction', 'looking', 'staring', 'observing', 'gazing', 'watching', 'shock', 'listening']): return 'Reaction / Observation'
return 'Static / Establishing'
@st.cache_data
def load_data():
df = pd.read_csv("horror_shot_database.csv")
# Apply standard categories
df['broad_camera'] = df['camera_angle'].apply(categorize_camera)
df['broad_mood'] = df['mood'].apply(categorize_mood)
df['broad_lighting'] = (df['mood'].fillna('') + " " + df['description'].fillna('')).apply(categorize_lighting)
# Apply new Storyboard categories
df['location_type'] = df.apply(categorize_location, axis=1)
df['subject_type'] = df.apply(categorize_subject, axis=1)
df['action_type'] = df.apply(categorize_action, axis=1)
return df
try:
df = load_data()
except Exception as e:
st.error(f"Error loading database: {e}")
st.stop()
# ==========================================
# 3. SHOTDECK-STYLE SEARCH & FILTERS
# ==========================================
st.sidebar.header("π Search Library")
search_query = st.sidebar.text_input("Keyword Search", placeholder="e.g., monster, shadow, weapon...")
st.sidebar.write("---")
st.sidebar.header("π Filter Categories")
# Expander 1: Location & Subjects
with st.sidebar.expander("π Location & Subjects", expanded=True):
all_locations = ["Any"] + sorted(df['location_type'].unique().tolist())
selected_location = st.selectbox("Setting", all_locations)
all_subjects = ["Any"] + sorted(df['subject_type'].unique().tolist())
selected_subject = st.selectbox("Characters in Frame", all_subjects)
# Expander 2: Scene & Action
with st.sidebar.expander("π¬ Action & Scene Type", expanded=True):
all_actions = ["Any"] + sorted(df['action_type'].unique().tolist())
selected_action = st.selectbox("Scene Action", all_actions)
# Expander 3: Camera
with st.sidebar.expander("π₯ Camera & Framing"):
all_angles = ["Any"] + sorted(df['broad_camera'].unique().tolist())
selected_angle = st.selectbox("Shot Type", all_angles)
# Expander 4: Atmosphere
with st.sidebar.expander("π Atmosphere"):
all_lighting = ["Any"] + sorted(df['broad_lighting'].unique().tolist())
selected_lighting = st.selectbox("Lighting Style", all_lighting)
all_moods = ["Any"] + sorted(df['broad_mood'].unique().tolist())
selected_mood = st.selectbox("Mood", all_moods)
# ==========================================
# 4. FILTERING LOGIC
# ==========================================
results = df.copy()
if search_query:
results = results[results['description'].str.contains(search_query, case=False, na=False)]
if selected_location != "Any":
results = results[results['location_type'] == selected_location]
if selected_subject != "Any":
results = results[results['subject_type'] == selected_subject]
if selected_action != "Any":
results = results[results['action_type'] == selected_action]
if selected_angle != "Any":
results = results[results['broad_camera'] == selected_angle]
if selected_lighting != "Any":
results = results[results['broad_lighting'] == selected_lighting]
if selected_mood != "Any":
results = results[results['broad_mood'] == selected_mood]
base_url = "https://huggingface.co/datasets/Roshanurs/Horror-Reference-Data/resolve/main/Panels_Out"
valid_images = []
for idx, row in results.iterrows():
img_url = f"{base_url}/{row['filename']}"
valid_images.append({
"url": img_url,
"filename": row['filename'],
"desc": row['description']
})
st.markdown(f"**Found {len(valid_images)} matching shots**")
st.write("---")
# ==========================================
# 5. THE MASONRY GALLERY
# ==========================================
if len(valid_images) > 0:
display_limit = 100
display_list = valid_images[:display_limit]
cols = st.columns(4)
for index, img_data in enumerate(display_list):
with cols[index % 4]:
st.image(img_data["url"], use_container_width=True)
st.markdown(
f'<a href="{img_data["url"]}" target="_blank">'
f'<button style="width:100%; padding:8px; border-radius:4px; border:1px solid #444; background:#222; color:white; cursor:pointer;">'
f'View Full Size</button></a>',
unsafe_allow_html=True
)
st.write("")
if len(valid_images) > display_limit:
st.info(f"Showing first {display_limit} results. Refine your filters to see more specific shots.")
else:
st.warning("No shots found matching those exact parameters. Try widening your search!") |