DeepSeekOracle commited on
Commit
908d6ef
Β·
verified Β·
1 Parent(s): dbff8e8

Create lygo_profile.py

Browse files
Files changed (1) hide show
  1. lygo_profile.py +286 -0
lygo_profile.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ LYGO Profile Generator v0.3
4
+ Image β†’ Musical DNA + Lyrical Framework
5
+
6
+ Extracts visual mathematics from an image and translates it into
7
+ structured creative direction for music production and AI-assisted lyric writing.
8
+ """
9
+
10
+ import cv2
11
+ import numpy as np
12
+ import json
13
+ import math
14
+ import argparse
15
+ from pathlib import Path
16
+ from datetime import datetime
17
+ from typing import Dict, Any, Optional
18
+
19
+ __version__ = "0.3.0"
20
+
21
+
22
+ class LYGOProfileGenerator:
23
+ def __init__(self, verbose: bool = True):
24
+ self.verbose = verbose
25
+
26
+ def _log(self, msg: str):
27
+ if self.verbose:
28
+ print(msg)
29
+
30
+ def analyze_image(self, image_path: str) -> Dict[str, Any]:
31
+ """Extract rich mathematical features from the image."""
32
+ img = cv2.imread(str(image_path))
33
+ if img is None:
34
+ raise FileNotFoundError(f"Image not found: {image_path}")
35
+
36
+ if len(img.shape) == 2:
37
+ img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
38
+
39
+ h, w, _ = img.shape
40
+ total_pixels = h * w
41
+
42
+ # === Color Analysis (HSV) ===
43
+ hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
44
+ avg_hue = float(np.mean(hsv[:, :, 0]) * 2) # 0-360
45
+ avg_sat = float(np.mean(hsv[:, :, 1]) / 255.0)
46
+ avg_val = float(np.mean(hsv[:, :, 2]) / 255.0)
47
+ sat_std = float(np.std(hsv[:, :, 1]) / 255.0) # colorfulness
48
+
49
+ # === Luminance & Contrast ===
50
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
51
+ brightness = float(np.mean(gray) / 255.0)
52
+ contrast = float(np.std(gray) / 255.0)
53
+
54
+ # === Structural Analysis ===
55
+ edges = cv2.Canny(gray, 50, 150)
56
+ edge_density = float(np.count_nonzero(edges) / total_pixels)
57
+
58
+ # Contours for structural complexity
59
+ contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
60
+ structure_index = min(len(contours) / 80.0, 1.0)
61
+
62
+ # Micro-chaos (FAST corners)
63
+ fast = cv2.FastFeatureDetector_create(threshold=38)
64
+ keypoints = fast.detect(gray, None)
65
+ chaos_index = len(keypoints)
66
+
67
+ features = {
68
+ "source_image": str(Path(image_path).name),
69
+ "dimensions": {"width": w, "height": h},
70
+ "color": {
71
+ "average_hue": round(avg_hue, 2),
72
+ "average_saturation": round(avg_sat, 4),
73
+ "average_brightness": round(brightness, 4),
74
+ "colorfulness": round(sat_std, 4),
75
+ },
76
+ "structure": {
77
+ "edge_density": round(edge_density, 4),
78
+ "contrast": round(contrast, 4),
79
+ "structure_index": round(structure_index, 4),
80
+ "chaos_keypoints": chaos_index,
81
+ },
82
+ }
83
+ return features
84
+
85
+ def _get_musical_key(self, hue: float, brightness: float) -> str:
86
+ keys = ["C", "G", "D", "A", "E", "B", "F#", "Db", "Ab", "Eb", "Bb", "F"]
87
+ key_index = int(hue / 30) % 12
88
+ mode = "Minor" if brightness < 0.48 else "Major"
89
+ return f"{keys[key_index]} {mode}"
90
+
91
+ def _calculate_bpm(self, edge_density: float, chaos: int, brightness: float) -> int:
92
+ base = 82 + (edge_density * 920)
93
+ chaos_mod = min(chaos / 1800, 0.6)
94
+ brightness_mod = (brightness - 0.5) * 12
95
+ bpm = int(base + (chaos_mod * 25) + brightness_mod)
96
+ return max(78, min(178, bpm))
97
+
98
+ def _generate_genre_texture(self, features: Dict) -> Dict[str, str]:
99
+ e = features["structure"]["edge_density"]
100
+ c = features["structure"]["chaos_keypoints"]
101
+ b = features["color"]["average_brightness"]
102
+ contrast = features["structure"]["contrast"]
103
+
104
+ if c > 650 and b < 0.38:
105
+ genre = "Industrial Dubstep / Dark Phonk"
106
+ texture = "Heavy distortion, aggressive stutters, deep sub-bass, metallic textures"
107
+ energy = "High-aggression"
108
+ elif e > 0.065 and contrast > 0.18:
109
+ genre = "Emo Rap / Modern Trap"
110
+ texture = "Crisp hi-hats, melancholic melodies, heavy 808s, emotional vocal layers"
111
+ energy = "Mid-High emotional"
112
+ elif c > 420 and b > 0.55:
113
+ genre = "Experimental / Glitch Hop"
114
+ texture = "Glitchy percussion, chopped vocals, atmospheric synths, rhythmic complexity"
115
+ energy = "High chaotic"
116
+ elif e < 0.035 and b > 0.6:
117
+ genre = "West Coast G-Funk / Smooth Instrumental"
118
+ texture = "Laid-back grooves, warm analog bass, melodic leads, nostalgic atmosphere"
119
+ energy = "Mid relaxed"
120
+ else:
121
+ genre = "Dark Alternative / Cinematic Rap"
122
+ texture = "Atmospheric pads, punchy drums, moody synths, introspective energy"
123
+ energy = "Mid cinematic"
124
+
125
+ return {"genre": genre, "texture": texture, "energy": energy}
126
+
127
+ def translate_to_lygo(self, features: Dict[str, Any]) -> Dict[str, Any]:
128
+ """Convert visual features into musical and lyrical creative direction."""
129
+ hue = features["color"]["average_hue"]
130
+ brightness = features["color"]["average_brightness"]
131
+ edge_density = features["structure"]["edge_density"]
132
+ chaos = features["structure"]["chaos_keypoints"]
133
+ contrast = features["structure"]["contrast"]
134
+
135
+ musical_key = self._get_musical_key(hue, brightness)
136
+ bpm = self._calculate_bpm(edge_density, chaos, brightness)
137
+ genre_data = self._generate_genre_texture(features)
138
+
139
+ # === Lyrical Theme Engine ===
140
+ if brightness < 0.42 and edge_density > 0.055:
141
+ core_theme = "Survival, betrayal, lone wolf resilience, moving in silence"
142
+ lyric_prompt = (
143
+ "Write raw, introspective lyrics about being the last one standing after betrayal. "
144
+ "Focus on trust issues, a very small circle of ride-or-die people, and the cold satisfaction of outlasting everyone who counted you out."
145
+ )
146
+ vocal_style = "Raspy melodic rap or gritty sung-rap hybrid"
147
+ elif chaos > 550:
148
+ core_theme = "Breaking chains, system resistance, unchained personal power"
149
+ lyric_prompt = (
150
+ "Write aggressive yet intelligent lyrics about breaking free from systems that tried to define you. "
151
+ "Emphasize resilience, moving in silence, and turning pain into unstoppable momentum."
152
+ )
153
+ vocal_style = "Assertive rap with melodic moments or distorted vocal processing"
154
+ else:
155
+ core_theme = "Observation, loyalty, navigating a cold modern world with quiet edge"
156
+ lyric_prompt = (
157
+ "Write clever, slightly dark observational lyrics with dry humor about modern life, loyalty, "
158
+ "and staying true to your own code while everything around you feels artificial."
159
+ )
160
+ vocal_style = "Deadpan to melodic rap delivery, slightly introspective"
161
+
162
+ # === Final Structured Output ===
163
+ lygo_profile = {
164
+ "LYGO_PROFILE": {
165
+ "version": __version__,
166
+ "generated_at": datetime.now().isoformat(),
167
+ "source": features["source_image"],
168
+ "mathematics": features,
169
+ "musical_dna": {
170
+ "root_key": musical_key,
171
+ "bpm": bpm,
172
+ "energy_level": genre_data["energy"],
173
+ "suggested_genre": genre_data["genre"],
174
+ "texture_description": genre_data["texture"],
175
+ "vocal_style": vocal_style,
176
+ },
177
+ "lyrical_framework": {
178
+ "core_theme": core_theme,
179
+ "ai_lyric_prompt": lyric_prompt,
180
+ },
181
+ "ai_music_prompt": (
182
+ f"Create a {genre_data['genre']} track at {bpm} BPM in the key of {musical_key}. "
183
+ f"The overall energy should feel {genre_data['energy'].lower()}. "
184
+ f"Sound design and texture: {genre_data['texture']}. "
185
+ f"Lyrical themes should center around {core_theme}."
186
+ ),
187
+ "production_notes": (
188
+ f"High contrast and structural complexity suggest strong dynamic range. "
189
+ f"Consider heavy low-end support and atmospheric layers to match the visual weight."
190
+ ),
191
+ }
192
+ }
193
+ return lygo_profile
194
+
195
+ def generate(self, image_path: str, output_json: str = "lygo_profile.json", create_brief: bool = False):
196
+ self._log(f"\n╔════════════════════════════════════════════╗")
197
+ self._log(f"β•‘ LYGO Profile Generator v{__version__} β•‘")
198
+ self._log(f"β•‘ Image β†’ Musical DNA + Lyrical Frameworkβ•‘")
199
+ self._log(f"β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n")
200
+
201
+ features = self.analyze_image(image_path)
202
+ profile = self.translate_to_lygo(features)
203
+
204
+ # Save JSON
205
+ with open(output_json, "w") as f:
206
+ json.dump(profile, f, indent=2)
207
+
208
+ self._log(json.dumps(profile, indent=2))
209
+ self._log(f"\n[+] LYGO Profile saved β†’ {output_json}")
210
+
211
+ if create_brief:
212
+ brief_path = Path(output_json).with_suffix(".brief.txt")
213
+ self._create_creative_brief(profile, brief_path)
214
+ self._log(f"[+] Creative Brief saved β†’ {brief_path}")
215
+
216
+ def _create_creative_brief(self, profile: Dict, path: Path):
217
+ data = profile["LYGO_PROFILE"]
218
+ brief = f"""LYGO CREATIVE BRIEF
219
+ Generated: {data['generated_at']}
220
+ Source Image: {data['source']}
221
+
222
+ ══════════════════════════════════════════════
223
+ MUSICAL DNA
224
+ ══════════════════════════════════════════════
225
+ Key: {data['musical_dna']['root_key']}
226
+ BPM: {data['musical_dna']['bpm']}
227
+ Energy: {data['musical_dna']['energy_level']}
228
+ Genre Direction: {data['musical_dna']['suggested_genre']}
229
+
230
+ Texture & Vibe:
231
+ {data['musical_dna']['texture_description']}
232
+
233
+ Vocal Approach: {data['musical_dna']['vocal_style']}
234
+
235
+ ══════════════════════════════════════════════
236
+ LYRICAL DIRECTION
237
+ ══════════════════════════════════════════════
238
+ Core Theme: {data['lyrical_framework']['core_theme']}
239
+
240
+ AI Prompt:
241
+ {data['lyrical_framework']['ai_lyric_prompt']}
242
+
243
+ ══════════════════════════════════════════════
244
+ FULL AI MUSIC PROMPT (Copy-Paste Ready)
245
+ ══════════════════════════════════════════════
246
+ {data['ai_music_prompt']}
247
+
248
+ Production Notes:
249
+ {data['production_notes']}
250
+ """
251
+ path.write_text(brief, encoding='utf-8')
252
+
253
+
254
+ def main():
255
+ parser = argparse.ArgumentParser(
256
+ description="LYGO Profile Generator β€” Turn any image into structured musical + lyrical creative direction"
257
+ )
258
+ parser.add_argument("image", help="Path to input image")
259
+ parser.add_argument("-o", "--output", default="lygo_profile.json", help="Output JSON file")
260
+ parser.add_argument("--brief", action="store_true", help="Also generate a human-readable .brief.txt file")
261
+ parser.add_argument("--batch", action="store_true", help="Process all images in a folder")
262
+ parser.add_argument("--quiet", action="store_true", help="Suppress console output")
263
+ args = parser.parse_args()
264
+
265
+ generator = LYGOProfileGenerator(verbose=not args.quiet)
266
+
267
+ if args.batch:
268
+ folder = Path(args.image)
269
+ if not folder.is_dir():
270
+ print("Error: --batch requires a folder path")
271
+ return
272
+ images = sorted(folder.glob("*.jpg")) + sorted(folder.glob("*.png")) + sorted(folder.glob("*.jpeg"))
273
+ if not images:
274
+ print("No images found in folder")
275
+ return
276
+ for img in images:
277
+ print(f"\nProcessing: {img.name}")
278
+ out_json = f"lygo_profile_{img.stem}.json"
279
+ generator.generate(str(img), out_json, create_brief=args.brief)
280
+ return
281
+
282
+ generator.generate(args.image, args.output, create_brief=args.brief)
283
+
284
+
285
+ if __name__ == "__main__":
286
+ main()