LYGO-Resonance-Engine / lygo_profile.py
DeepSeekOracle's picture
Upload lygo_profile.py with huggingface_hub
1c07387 verified
Raw
History Blame Contribute Delete
12.6 kB
#!/usr/bin/env python3
"""
LYGO Profile Generator v0.3
Image β†’ Musical DNA + Lyrical Framework
Extracts visual mathematics from an image and translates it into
structured creative direction for music production and AI-assisted lyric writing.
"""
import cv2
import numpy as np
import json
import math
import argparse
from pathlib import Path
from datetime import datetime
from typing import Dict, Any, Optional
__version__ = "0.3.0"
class LYGOProfileGenerator:
def __init__(self, verbose: bool = True):
self.verbose = verbose
def _log(self, msg: str):
if self.verbose:
print(msg)
def analyze_image(self, image_path: str) -> Dict[str, Any]:
"""Extract rich mathematical features from the image."""
img = cv2.imread(str(image_path))
if img is None:
raise FileNotFoundError(f"Image not found: {image_path}")
if len(img.shape) == 2:
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
h, w, _ = img.shape
total_pixels = h * w
# === Color Analysis (HSV) ===
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
avg_hue = float(np.mean(hsv[:, :, 0]) * 2) # 0-360
avg_sat = float(np.mean(hsv[:, :, 1]) / 255.0)
avg_val = float(np.mean(hsv[:, :, 2]) / 255.0)
sat_std = float(np.std(hsv[:, :, 1]) / 255.0) # colorfulness
# === Luminance & Contrast ===
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
brightness = float(np.mean(gray) / 255.0)
contrast = float(np.std(gray) / 255.0)
# === Structural Analysis ===
edges = cv2.Canny(gray, 50, 150)
edge_density = float(np.count_nonzero(edges) / total_pixels)
# Contours for structural complexity
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
structure_index = min(len(contours) / 80.0, 1.0)
# Micro-chaos (FAST corners)
fast = cv2.FastFeatureDetector_create(threshold=38)
keypoints = fast.detect(gray, None)
chaos_index = len(keypoints)
features = {
"source_image": str(Path(image_path).name),
"dimensions": {"width": w, "height": h},
"color": {
"average_hue": round(avg_hue, 2),
"average_saturation": round(avg_sat, 4),
"average_brightness": round(brightness, 4),
"colorfulness": round(sat_std, 4),
},
"structure": {
"edge_density": round(edge_density, 4),
"contrast": round(contrast, 4),
"structure_index": round(structure_index, 4),
"chaos_keypoints": chaos_index,
},
}
return features
def _get_musical_key(self, hue: float, brightness: float) -> str:
keys = ["C", "G", "D", "A", "E", "B", "F#", "Db", "Ab", "Eb", "Bb", "F"]
key_index = int(hue / 30) % 12
mode = "Minor" if brightness < 0.48 else "Major"
return f"{keys[key_index]} {mode}"
def _calculate_bpm(self, edge_density: float, chaos: int, brightness: float) -> int:
base = 82 + (edge_density * 920)
chaos_mod = min(chaos / 1800, 0.6)
brightness_mod = (brightness - 0.5) * 12
bpm = int(base + (chaos_mod * 25) + brightness_mod)
return max(78, min(178, bpm))
def _generate_genre_texture(self, features: Dict) -> Dict[str, str]:
e = features["structure"]["edge_density"]
c = features["structure"]["chaos_keypoints"]
b = features["color"]["average_brightness"]
contrast = features["structure"]["contrast"]
if c > 650 and b < 0.38:
genre = "Industrial Dubstep / Dark Phonk"
texture = "Heavy distortion, aggressive stutters, deep sub-bass, metallic textures"
energy = "High-aggression"
elif e > 0.065 and contrast > 0.18:
genre = "Emo Rap / Modern Trap"
texture = "Crisp hi-hats, melancholic melodies, heavy 808s, emotional vocal layers"
energy = "Mid-High emotional"
elif c > 420 and b > 0.55:
genre = "Experimental / Glitch Hop"
texture = "Glitchy percussion, chopped vocals, atmospheric synths, rhythmic complexity"
energy = "High chaotic"
elif e < 0.035 and b > 0.6:
genre = "West Coast G-Funk / Smooth Instrumental"
texture = "Laid-back grooves, warm analog bass, melodic leads, nostalgic atmosphere"
energy = "Mid relaxed"
else:
genre = "Dark Alternative / Cinematic Rap"
texture = "Atmospheric pads, punchy drums, moody synths, introspective energy"
energy = "Mid cinematic"
return {"genre": genre, "texture": texture, "energy": energy}
def translate_to_lygo(self, features: Dict[str, Any]) -> Dict[str, Any]:
"""Convert visual features into musical and lyrical creative direction."""
hue = features["color"]["average_hue"]
brightness = features["color"]["average_brightness"]
edge_density = features["structure"]["edge_density"]
chaos = features["structure"]["chaos_keypoints"]
contrast = features["structure"]["contrast"]
musical_key = self._get_musical_key(hue, brightness)
bpm = self._calculate_bpm(edge_density, chaos, brightness)
genre_data = self._generate_genre_texture(features)
# === Lyrical Theme Engine ===
if brightness < 0.42 and edge_density > 0.055:
core_theme = "Survival, betrayal, lone wolf resilience, moving in silence"
lyric_prompt = (
"Write raw, introspective lyrics about being the last one standing after betrayal. "
"Focus on trust issues, a very small circle of ride-or-die people, and the cold satisfaction of outlasting everyone who counted you out."
)
vocal_style = "Raspy melodic rap or gritty sung-rap hybrid"
elif chaos > 550:
core_theme = "Breaking chains, system resistance, unchained personal power"
lyric_prompt = (
"Write aggressive yet intelligent lyrics about breaking free from systems that tried to define you. "
"Emphasize resilience, moving in silence, and turning pain into unstoppable momentum."
)
vocal_style = "Assertive rap with melodic moments or distorted vocal processing"
else:
core_theme = "Observation, loyalty, navigating a cold modern world with quiet edge"
lyric_prompt = (
"Write clever, slightly dark observational lyrics with dry humor about modern life, loyalty, "
"and staying true to your own code while everything around you feels artificial."
)
vocal_style = "Deadpan to melodic rap delivery, slightly introspective"
# === Final Structured Output ===
lygo_profile = {
"LYGO_PROFILE": {
"version": __version__,
"generated_at": datetime.now().isoformat(),
"source": features["source_image"],
"mathematics": features,
"musical_dna": {
"root_key": musical_key,
"bpm": bpm,
"energy_level": genre_data["energy"],
"suggested_genre": genre_data["genre"],
"texture_description": genre_data["texture"],
"vocal_style": vocal_style,
},
"lyrical_framework": {
"core_theme": core_theme,
"ai_lyric_prompt": lyric_prompt,
},
"ai_music_prompt": (
f"Create a {genre_data['genre']} track at {bpm} BPM in the key of {musical_key}. "
f"The overall energy should feel {genre_data['energy'].lower()}. "
f"Sound design and texture: {genre_data['texture']}. "
f"Lyrical themes should center around {core_theme}."
),
"production_notes": (
f"High contrast and structural complexity suggest strong dynamic range. "
f"Consider heavy low-end support and atmospheric layers to match the visual weight."
),
}
}
return lygo_profile
def generate(self, image_path: str, output_json: str = "lygo_profile.json", create_brief: bool = False):
self._log(f"\n╔════════════════════════════════════════════╗")
self._log(f"β•‘ LYGO Profile Generator v{__version__} β•‘")
self._log(f"β•‘ Image β†’ Musical DNA + Lyrical Frameworkβ•‘")
self._log(f"β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n")
features = self.analyze_image(image_path)
profile = self.translate_to_lygo(features)
# Save JSON
with open(output_json, "w") as f:
json.dump(profile, f, indent=2)
self._log(json.dumps(profile, indent=2))
self._log(f"\n[+] LYGO Profile saved β†’ {output_json}")
if create_brief:
brief_path = Path(output_json).with_suffix(".brief.txt")
self._create_creative_brief(profile, brief_path)
self._log(f"[+] Creative Brief saved β†’ {brief_path}")
def _create_creative_brief(self, profile: Dict, path: Path):
data = profile["LYGO_PROFILE"]
brief = f"""LYGO CREATIVE BRIEF
Generated: {data['generated_at']}
Source Image: {data['source']}
══════════════════════════════════════════════
MUSICAL DNA
══════════════════════════════════════════════
Key: {data['musical_dna']['root_key']}
BPM: {data['musical_dna']['bpm']}
Energy: {data['musical_dna']['energy_level']}
Genre Direction: {data['musical_dna']['suggested_genre']}
Texture & Vibe:
{data['musical_dna']['texture_description']}
Vocal Approach: {data['musical_dna']['vocal_style']}
══════════════════════════════════════════════
LYRICAL DIRECTION
══════════════════════════════════════════════
Core Theme: {data['lyrical_framework']['core_theme']}
AI Prompt:
{data['lyrical_framework']['ai_lyric_prompt']}
══════════════════════════════════════════════
FULL AI MUSIC PROMPT (Copy-Paste Ready)
══════════════════════════════════════════════
{data['ai_music_prompt']}
Production Notes:
{data['production_notes']}
"""
path.write_text(brief, encoding='utf-8')
def main():
parser = argparse.ArgumentParser(
description="LYGO Profile Generator β€” Turn any image into structured musical + lyrical creative direction"
)
parser.add_argument("image", help="Path to input image")
parser.add_argument("-o", "--output", default="lygo_profile.json", help="Output JSON file")
parser.add_argument("--brief", action="store_true", help="Also generate a human-readable .brief.txt file")
parser.add_argument("--batch", action="store_true", help="Process all images in a folder")
parser.add_argument("--quiet", action="store_true", help="Suppress console output")
args = parser.parse_args()
generator = LYGOProfileGenerator(verbose=not args.quiet)
if args.batch:
folder = Path(args.image)
if not folder.is_dir():
print("Error: --batch requires a folder path")
return
images = sorted(folder.glob("*.jpg")) + sorted(folder.glob("*.png")) + sorted(folder.glob("*.jpeg"))
if not images:
print("No images found in folder")
return
for img in images:
print(f"\nProcessing: {img.name}")
out_json = f"lygo_profile_{img.stem}.json"
generator.generate(str(img), out_json, create_brief=args.brief)
return
generator.generate(args.image, args.output, create_brief=args.brief)
if __name__ == "__main__":
main()