mergekit
Merge
Mistral_Star
Mistral_Quiet
Mistral
Mixtral
Question-Answer
Token-Classification
Sequence-Classification
SpydazWeb-AI
chemistry
biology
legal
code
climate
medical
LCARS_AI_StarTrek_Computer
text-generation-inference
chain-of-thought
tree-of-knowledge
forest-of-thoughts
visual-spacial-sketchpad
alpha-mind
knowledge-graph
entity-detection
encyclopedia
wikipedia
stack-exchange
Reddit
Cyber-series
MegaMind
Cybertron
SpydazWeb
Spydaz
LCARS
star-trek
mega-transformers
Mulit-Mega-Merge
Multi-Lingual
Afro-Centric
African-Model
Ancient-One
Upload 2 files
Browse files- Spectrograms.py +384 -0
- imageTotext-gradio.py +288 -0
Spectrograms.py
ADDED
@@ -0,0 +1,384 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import torch
|
3 |
+
import torchaudio
|
4 |
+
import librosa
|
5 |
+
import librosa.display
|
6 |
+
import matplotlib.pyplot as plt
|
7 |
+
import soundfile as sf
|
8 |
+
from PIL import Image
|
9 |
+
|
10 |
+
|
11 |
+
# Step 1: Encode Audio to Mel-Spectrogram
|
12 |
+
def encode_audio_to_mel_spectrogram(audio_file, n_mels=128):
|
13 |
+
"""
|
14 |
+
Encode an audio file to a mel-spectrogram.
|
15 |
+
|
16 |
+
Parameters:
|
17 |
+
- audio_file: Path to the audio file.
|
18 |
+
- n_mels: Number of mel bands (default: 128).
|
19 |
+
|
20 |
+
Returns:
|
21 |
+
- mel_spectrogram_db: Mel-spectrogram in dB scale.
|
22 |
+
- sample_rate: Sample rate of the audio file.
|
23 |
+
"""
|
24 |
+
y, sample_rate = librosa.load(audio_file, sr=None) # Load audio
|
25 |
+
mel_spectrogram = librosa.feature.melspectrogram(y=y, sr=sample_rate, n_mels=n_mels)
|
26 |
+
mel_spectrogram_db = librosa.power_to_db(mel_spectrogram, ref=np.max) # Convert to dB
|
27 |
+
return mel_spectrogram_db, sample_rate
|
28 |
+
|
29 |
+
# Improved Step 2: Save Mel-Spectrogram as Image
|
30 |
+
def save_mel_spectrogram_image(mel_spectrogram_db, sample_rate, output_image='mel_spectrogram.png', method='matplotlib', figsize=(10, 4), cmap='hot'):
|
31 |
+
"""
|
32 |
+
Save the mel-spectrogram as an image using the specified method.
|
33 |
+
|
34 |
+
Parameters:
|
35 |
+
- mel_spectrogram_db: Mel-spectrogram in dB scale.
|
36 |
+
- sample_rate: Sample rate of the audio file.
|
37 |
+
- output_image: Path to save the image.
|
38 |
+
- method: Method for saving ('matplotlib' or 'custom').
|
39 |
+
- figsize: Size of the figure for matplotlib (default: (10, 4)).
|
40 |
+
- cmap: Colormap for the spectrogram (default: 'hot').
|
41 |
+
"""
|
42 |
+
if method == 'matplotlib':
|
43 |
+
plt.figure(figsize=figsize)
|
44 |
+
librosa.display.specshow(mel_spectrogram_db, sr=sample_rate, x_axis='time', y_axis='mel', cmap=cmap)
|
45 |
+
plt.colorbar(format='%+2.0f dB')
|
46 |
+
plt.title('Mel-Spectrogram')
|
47 |
+
plt.savefig(output_image)
|
48 |
+
plt.close()
|
49 |
+
print(f"Mel-spectrogram image saved using matplotlib as '{output_image}'")
|
50 |
+
|
51 |
+
elif method == 'custom':
|
52 |
+
# Convert dB scale to linear scale for image generation
|
53 |
+
mel_spectrogram_linear = librosa.db_to_power(mel_spectrogram_db)
|
54 |
+
# Create an image from the mel-spectrogram
|
55 |
+
image = image_from_spectrogram(mel_spectrogram_linear[np.newaxis, ...]) # Add channel dimension
|
56 |
+
# Save the image
|
57 |
+
image.save(output_image)
|
58 |
+
print(f"Mel-spectrogram image saved using custom method as '{output_image}'")
|
59 |
+
|
60 |
+
else:
|
61 |
+
raise ValueError("Invalid method. Choose 'matplotlib' or 'custom'.")
|
62 |
+
|
63 |
+
|
64 |
+
# Spectrogram conversion functions
|
65 |
+
def image_from_spectrogram(spectrogram: np.ndarray, power: float = 0.25) -> Image.Image:
|
66 |
+
"""
|
67 |
+
Compute a spectrogram image from a spectrogram magnitude array.
|
68 |
+
|
69 |
+
Args:
|
70 |
+
spectrogram: (channels, frequency, time)
|
71 |
+
power: A power curve to apply to the spectrogram to preserve contrast
|
72 |
+
|
73 |
+
Returns:
|
74 |
+
image: (frequency, time, channels)
|
75 |
+
"""
|
76 |
+
# Rescale to 0-1
|
77 |
+
max_value = np.max(spectrogram)
|
78 |
+
data = spectrogram / max_value
|
79 |
+
|
80 |
+
# Apply the power curve
|
81 |
+
data = np.power(data, power)
|
82 |
+
|
83 |
+
# Rescale to 0-255 and invert
|
84 |
+
data = 255 - (data * 255).astype(np.uint8)
|
85 |
+
|
86 |
+
# Convert to a PIL image
|
87 |
+
if data.shape[0] == 1:
|
88 |
+
image = Image.fromarray(data[0], mode="L").convert("RGB")
|
89 |
+
elif data.shape[0] == 2:
|
90 |
+
data = np.array([np.zeros_like(data[0]), data[0], data[1]]).transpose(1, 2, 0)
|
91 |
+
image = Image.fromarray(data, mode="RGB")
|
92 |
+
else:
|
93 |
+
raise NotImplementedError(f"Unsupported number of channels: {data.shape[0]}")
|
94 |
+
|
95 |
+
# Flip Y
|
96 |
+
image = image.transpose(Image.FLIP_TOP_BOTTOM)
|
97 |
+
return image
|
98 |
+
|
99 |
+
|
100 |
+
# Step 3: Extract Mel-Spectrogram from Image (Direct Pixel Manipulation)
|
101 |
+
def extract_mel_spectrogram_from_image(image_path):
|
102 |
+
"""
|
103 |
+
Extract a mel-spectrogram from a saved image using pixel manipulation.
|
104 |
+
|
105 |
+
Parameters:
|
106 |
+
- image_path: Path to the spectrogram image file.
|
107 |
+
|
108 |
+
Returns:
|
109 |
+
- mel_spectrogram_db: The extracted mel-spectrogram in dB scale.
|
110 |
+
"""
|
111 |
+
img = Image.open(image_path).convert('L') # Open image and convert to grayscale
|
112 |
+
img_array = np.array(img) # Convert to NumPy array
|
113 |
+
mel_spectrogram_db = img_array / 255.0 * -80 # Scale to dB range
|
114 |
+
return mel_spectrogram_db
|
115 |
+
|
116 |
+
# Alternative Spectrogram Extraction (IFFT Method)
|
117 |
+
def extract_spectrogram_with_ifft(mel_spectrogram_db):
|
118 |
+
"""
|
119 |
+
Extracts the audio signal from a mel-spectrogram using the inverse FFT method.
|
120 |
+
|
121 |
+
Parameters:
|
122 |
+
- mel_spectrogram_db: The mel-spectrogram in dB scale.
|
123 |
+
|
124 |
+
Returns:
|
125 |
+
- audio: The reconstructed audio signal.
|
126 |
+
"""
|
127 |
+
# Convert dB mel-spectrogram back to linear scale
|
128 |
+
mel_spectrogram = librosa.db_to_power(mel_spectrogram_db)
|
129 |
+
|
130 |
+
# Inverse mel transformation to get the audio signal
|
131 |
+
# Using IFFT (simplified for demonstration; typically requires phase info)
|
132 |
+
audio = librosa.feature.inverse.mel_to_audio(mel_spectrogram)
|
133 |
+
|
134 |
+
return audio
|
135 |
+
|
136 |
+
# Step 4: Decode Mel-Spectrogram with Griffin-Lim
|
137 |
+
def decode_mel_spectrogram_to_audio(mel_spectrogram_db, sample_rate, output_audio='griffin_reconstructed_audio.wav'):
|
138 |
+
"""
|
139 |
+
Decode a mel-spectrogram into audio using Griffin-Lim algorithm.
|
140 |
+
|
141 |
+
Parameters:
|
142 |
+
- mel_spectrogram_db: The mel-spectrogram in dB scale.
|
143 |
+
- sample_rate: The sample rate for the audio file.
|
144 |
+
- output_audio: Path to save the reconstructed audio file.
|
145 |
+
"""
|
146 |
+
# Convert dB mel-spectrogram back to linear scale
|
147 |
+
mel_spectrogram = librosa.db_to_power(mel_spectrogram_db)
|
148 |
+
# Perform Griffin-Lim to reconstruct audio
|
149 |
+
audio = librosa.griffinlim(mel_spectrogram)
|
150 |
+
# Save the generated audio
|
151 |
+
sf.write(output_audio, audio, sample_rate)
|
152 |
+
print(f"Griffin-Lim reconstructed audio saved as '{output_audio}'")
|
153 |
+
return audio
|
154 |
+
|
155 |
+
# Step 5: Load MelGAN Vocoder
|
156 |
+
def load_melgan_vocoder():
|
157 |
+
"""
|
158 |
+
Load a lightweight pre-trained MelGAN vocoder for decoding mel-spectrograms.
|
159 |
+
Returns a torch MelGAN vocoder model.
|
160 |
+
"""
|
161 |
+
model = torchaudio.models.MelGAN() # Load MelGAN model
|
162 |
+
model.eval() # Ensure the model is in evaluation mode
|
163 |
+
return model
|
164 |
+
|
165 |
+
# Step 6: Decode Mel-Spectrogram with MelGAN
|
166 |
+
def decode_mel_spectrogram_with_melgan(mel_spectrogram_db, sample_rate, output_audio='melgan_reconstructed_audio.wav'):
|
167 |
+
"""
|
168 |
+
Decode a mel-spectrogram into audio using MelGAN vocoder.
|
169 |
+
|
170 |
+
Parameters:
|
171 |
+
- mel_spectrogram_db: The mel-spectrogram in dB scale.
|
172 |
+
- sample_rate: The sample rate for the audio file.
|
173 |
+
- output_audio: Path to save the reconstructed audio file.
|
174 |
+
|
175 |
+
Returns:
|
176 |
+
- audio: The reconstructed audio signal.
|
177 |
+
"""
|
178 |
+
# Convert dB mel-spectrogram back to linear scale
|
179 |
+
mel_spectrogram = librosa.db_to_power(mel_spectrogram_db)
|
180 |
+
# Convert numpy array to torch tensor and adjust the shape
|
181 |
+
mel_spectrogram_tensor = torch.tensor(mel_spectrogram).unsqueeze(0) # Shape: [1, mel_bins, time_frames]
|
182 |
+
|
183 |
+
# Load the MelGAN vocoder model
|
184 |
+
melgan = load_melgan_vocoder()
|
185 |
+
|
186 |
+
# Pass the mel-spectrogram through MelGAN to generate audio
|
187 |
+
with torch.no_grad():
|
188 |
+
audio = melgan(mel_spectrogram_tensor).squeeze().numpy() # Squeeze to remove batch dimension
|
189 |
+
|
190 |
+
# Save the generated audio
|
191 |
+
sf.write(output_audio, audio, sample_rate)
|
192 |
+
print(f"MelGAN reconstructed audio saved as '{output_audio}'")
|
193 |
+
return audio
|
194 |
+
|
195 |
+
def audio_from_waveform(samples: np.ndarray, sample_rate: int, normalize: bool = False) -> pydub.AudioSegment:
|
196 |
+
"""
|
197 |
+
Convert a numpy array of samples of a waveform to an audio segment.
|
198 |
+
|
199 |
+
Args:
|
200 |
+
samples: (channels, samples) array
|
201 |
+
sample_rate: Sample rate of the audio.
|
202 |
+
normalize: Flag to normalize volume.
|
203 |
+
|
204 |
+
Returns:
|
205 |
+
pydub.AudioSegment
|
206 |
+
"""
|
207 |
+
# Normalize volume to fit in int16
|
208 |
+
if normalize:
|
209 |
+
samples *= np.iinfo(np.int16).max / np.max(np.abs(samples))
|
210 |
+
|
211 |
+
# Transpose and convert to int16
|
212 |
+
samples = samples.transpose(1, 0).astype(np.int16)
|
213 |
+
|
214 |
+
# Write to the bytes of a WAV file
|
215 |
+
wav_bytes = io.BytesIO()
|
216 |
+
wavfile.write(wav_bytes, sample_rate, samples)
|
217 |
+
wav_bytes.seek(0)
|
218 |
+
|
219 |
+
# Read into pydub
|
220 |
+
return pydub.AudioSegment.from_wav(wav_bytes)
|
221 |
+
|
222 |
+
|
223 |
+
def apply_filters(segment: pydub.AudioSegment, compression: bool = False) -> pydub.AudioSegment:
|
224 |
+
"""
|
225 |
+
Apply post-processing filters to the audio segment to compress it and keep at a -10 dBFS level.
|
226 |
+
|
227 |
+
Args:
|
228 |
+
segment: The audio segment to filter.
|
229 |
+
compression: Flag to apply dynamic range compression.
|
230 |
+
|
231 |
+
Returns:
|
232 |
+
pydub.AudioSegment
|
233 |
+
"""
|
234 |
+
if compression:
|
235 |
+
segment = pydub.effects.normalize(segment, headroom=0.1)
|
236 |
+
segment = segment.apply_gain(-10 - segment.dBFS)
|
237 |
+
segment = pydub.effects.compress_dynamic_range(
|
238 |
+
segment,
|
239 |
+
threshold=-20.0,
|
240 |
+
ratio=4.0,
|
241 |
+
attack=5.0,
|
242 |
+
release=50.0,
|
243 |
+
)
|
244 |
+
|
245 |
+
# Apply gain to desired dB level and normalize again
|
246 |
+
desired_db = -12
|
247 |
+
segment = segment.apply_gain(desired_db - segment.dBFS)
|
248 |
+
return pydub.effects.normalize(segment, headroom=0.1)
|
249 |
+
|
250 |
+
|
251 |
+
def stitch_segments(segments: Sequence[pydub.AudioSegment], crossfade_s: float) -> pydub.AudioSegment:
|
252 |
+
"""
|
253 |
+
Stitch together a sequence of audio segments with a crossfade between each segment.
|
254 |
+
|
255 |
+
Args:
|
256 |
+
segments: Sequence of audio segments to stitch.
|
257 |
+
crossfade_s: Duration of crossfade in seconds.
|
258 |
+
|
259 |
+
Returns:
|
260 |
+
pydub.AudioSegment
|
261 |
+
"""
|
262 |
+
crossfade_ms = int(crossfade_s * 1000)
|
263 |
+
combined_segment = segments[0]
|
264 |
+
for segment in segments[1:]:
|
265 |
+
combined_segment = combined_segment.append(segment, crossfade=crossfade_ms)
|
266 |
+
return combined_segment
|
267 |
+
|
268 |
+
|
269 |
+
def overlay_segments(segments: Sequence[pydub.AudioSegment]) -> pydub.AudioSegment:
|
270 |
+
"""
|
271 |
+
Overlay a sequence of audio segments on top of each other.
|
272 |
+
|
273 |
+
Args:
|
274 |
+
segments: Sequence of audio segments to overlay.
|
275 |
+
|
276 |
+
Returns:
|
277 |
+
pydub.AudioSegment
|
278 |
+
"""
|
279 |
+
assert len(segments) > 0
|
280 |
+
output: pydub.AudioSegment = segments[0]
|
281 |
+
for segment in segments[1:]:
|
282 |
+
output = output.overlay(segment)
|
283 |
+
return output
|
284 |
+
|
285 |
+
|
286 |
+
|
287 |
+
# Step 7: Full Pipeline for Audio Processing with Customization
|
288 |
+
def mel_spectrogram_pipeline(audio_file, output_image='mel_spectrogram.png',
|
289 |
+
output_audio_griffin='griffin_reconstructed_audio.wav',
|
290 |
+
output_audio_melgan='melgan_reconstructed_audio.wav',
|
291 |
+
extraction_method='pixel', # 'pixel' or 'ifft'
|
292 |
+
decoding_method='griffin'): # 'griffin' or 'melgan'
|
293 |
+
"""
|
294 |
+
Full pipeline to encode audio to mel-spectrogram, save it as an image, extract the spectrogram from the image,
|
295 |
+
and decode it back to audio using the selected methods.
|
296 |
+
|
297 |
+
Parameters:
|
298 |
+
- audio_file: Path to the audio file to be processed.
|
299 |
+
- output_image: Path to save the mel-spectrogram image (default: 'mel_spectrogram.png').
|
300 |
+
- output_audio_griffin: Path to save the Griffin-Lim reconstructed audio.
|
301 |
+
- output_audio_melgan: Path to save the MelGAN reconstructed audio.
|
302 |
+
- extraction_method: Method for extraction ('pixel' or 'ifft').
|
303 |
+
- decoding_method: Method for decoding ('griffin' or 'melgan').
|
304 |
+
"""
|
305 |
+
# Step 1: Encode (Audio -> Mel-Spectrogram)
|
306 |
+
mel_spectrogram_db, sample_rate = encode_audio_to_mel_spectrogram(audio_file)
|
307 |
+
|
308 |
+
# Step 2: Convert Mel-Spectrogram to Image and save it
|
309 |
+
save_mel_spectrogram_image(mel_spectrogram_db, sample_rate, output_image)
|
310 |
+
|
311 |
+
# Step 3: Extract Mel-Spectrogram from the image based on chosen method
|
312 |
+
if extraction_method == 'pixel':
|
313 |
+
extracted_mel_spectrogram_db = extract_mel_spectrogram_from_image(output_image)
|
314 |
+
elif extraction_method == 'ifft':
|
315 |
+
extracted_mel_spectrogram_db = extract_spectrogram_with_ifft(mel_spectrogram_db)
|
316 |
+
else:
|
317 |
+
raise ValueError("Invalid extraction method. Choose 'pixel' or 'ifft'.")
|
318 |
+
|
319 |
+
# Step 4: Decode based on the chosen decoding method
|
320 |
+
if decoding_method == 'griffin':
|
321 |
+
decode_mel_spectrogram_to_audio(extracted_mel_spectrogram_db, sample_rate, output_audio_griffin)
|
322 |
+
elif decoding_method == 'melgan':
|
323 |
+
decode_mel_spectrogram_with_melgan(extracted_mel_spectrogram_db, sample_rate, output_audio_melgan)
|
324 |
+
else:
|
325 |
+
raise ValueError("Invalid decoding method. Choose 'griffin' or 'melgan'.")
|
326 |
+
|
327 |
+
|
328 |
+
def process_audio(audio_file, extraction_method, decoding_method):
|
329 |
+
# Create temporary files for outputs
|
330 |
+
with tempfile.NamedTemporaryFile(suffix=".png") as temp_image, \
|
331 |
+
tempfile.NamedTemporaryFile(suffix=".wav") as temp_audio_griffin, \
|
332 |
+
tempfile.NamedTemporaryFile(suffix=".wav") as temp_audio_melgan:
|
333 |
+
|
334 |
+
# Step 1: Encode (Audio -> Mel-Spectrogram)
|
335 |
+
mel_spectrogram_db, sample_rate = encode_audio_to_mel_spectrogram(audio_file)
|
336 |
+
|
337 |
+
# Step 2: Convert Mel-Spectrogram to Image and save it
|
338 |
+
save_mel_spectrogram_image(mel_spectrogram_db, sample_rate, temp_image.name)
|
339 |
+
|
340 |
+
# Step 3: Extract Mel-Spectrogram from the image based on chosen method
|
341 |
+
if extraction_method == 'pixel':
|
342 |
+
extracted_mel_spectrogram_db = extract_mel_spectrogram_from_image(temp_image.name)
|
343 |
+
elif extraction_method == 'ifft':
|
344 |
+
extracted_mel_spectrogram_db = extract_spectrogram_with_ifft(mel_spectrogram_db)
|
345 |
+
|
346 |
+
# Step 4: Decode using both methods
|
347 |
+
decode_mel_spectrogram_to_audio(extracted_mel_spectrogram_db, sample_rate, temp_audio_griffin.name)
|
348 |
+
decode_mel_spectrogram_with_melgan(extracted_mel_spectrogram_db, sample_rate, temp_audio_melgan.name)
|
349 |
+
|
350 |
+
# Return results
|
351 |
+
return (temp_image.name,
|
352 |
+
temp_audio_griffin.name if decoding_method == 'griffin' else temp_audio_melgan.name)
|
353 |
+
|
354 |
+
# Create Gradio interface
|
355 |
+
iface = gr.Interface(
|
356 |
+
fn=process_audio,
|
357 |
+
inputs=[
|
358 |
+
gr.Audio(type="filepath", label="Upload Audio"),
|
359 |
+
gr.Radio(["pixel", "ifft"], label="Extraction Method", value="pixel"),
|
360 |
+
gr.Radio(["griffin", "melgan"], label="Decoding Method", value="griffin")
|
361 |
+
],
|
362 |
+
outputs=[
|
363 |
+
gr.Image(type="filepath", label="Mel-Spectrogram"),
|
364 |
+
gr.Audio(type="filepath", label="Reconstructed Audio")
|
365 |
+
],
|
366 |
+
title="Audio Encoder-Decoder",
|
367 |
+
description="Upload an audio file to encode it to a mel-spectrogram and then decode it back to audio."
|
368 |
+
)
|
369 |
+
|
370 |
+
# Launch the app
|
371 |
+
iface.launch()
|
372 |
+
|
373 |
+
|
374 |
+
# Example usage(TEST)
|
375 |
+
if __name__ == "__main__":
|
376 |
+
audio_file_path = 'your_audio_file.wav' # Specify the path to your audio file here
|
377 |
+
mel_spectrogram_pipeline(
|
378 |
+
audio_file_path,
|
379 |
+
output_image='mel_spectrogram.png',
|
380 |
+
output_audio_griffin='griffin_reconstructed_audio.wav',
|
381 |
+
output_audio_melgan='melgan_reconstructed_audio.wav',
|
382 |
+
extraction_method='pixel', # Choose 'pixel' or 'ifft'
|
383 |
+
decoding_method='griffin' # Choose 'griffin' or 'melgan'
|
384 |
+
)
|
imageTotext-gradio.py
ADDED
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import re
|
3 |
+
import uuid
|
4 |
+
import os
|
5 |
+
import requests
|
6 |
+
from PIL import Image
|
7 |
+
from PIL import ImageOps
|
8 |
+
from io import BytesIO
|
9 |
+
from urllib.parse import urlparse
|
10 |
+
from pathlib import Path
|
11 |
+
from tqdm import tqdm
|
12 |
+
import gradio as gr
|
13 |
+
from gradio.components import Textbox, Radio, Dataframe
|
14 |
+
import torch
|
15 |
+
from llava.constants import DEFAULT_IMAGE_TOKEN, IMAGE_TOKEN_INDEX
|
16 |
+
from llava.conversation import SeparatorStyle, conv_templates
|
17 |
+
from llava.mm_utils import (
|
18 |
+
KeywordsStoppingCriteria,
|
19 |
+
get_model_name_from_path,
|
20 |
+
process_images,
|
21 |
+
tokenizer_image_token,
|
22 |
+
)
|
23 |
+
from llava.model.builder import load_pretrained_model
|
24 |
+
from llava.utils import disable_torch_init
|
25 |
+
|
26 |
+
# Set CUDA device
|
27 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
|
28 |
+
|
29 |
+
disable_torch_init()
|
30 |
+
torch.manual_seed(1234)
|
31 |
+
|
32 |
+
# Load model and other necessary components
|
33 |
+
MODEL = "LeroyDyer/Mixtral_AI_Vision-Instruct_X"
|
34 |
+
model_name = get_model_name_from_path(MODEL)
|
35 |
+
tokenizer, model, image_processor, context_len = load_pretrained_model(
|
36 |
+
model_path=MODEL, model_base=None, model_name=model_name, device="cuda"
|
37 |
+
)
|
38 |
+
|
39 |
+
def get_extension_from_url(url):
|
40 |
+
"""
|
41 |
+
Extract the file extension from the given URL.
|
42 |
+
"""
|
43 |
+
parsed_url = urlparse(url)
|
44 |
+
path = Path(parsed_url.path)
|
45 |
+
return path.suffix
|
46 |
+
|
47 |
+
def remove_transparency(image):
|
48 |
+
if image.mode in ('RGBA', 'LA') or (image.mode == 'P' and 'transparency' in image.info):
|
49 |
+
alpha = image.convert('RGBA').split()[-1]
|
50 |
+
bg = Image.new("RGB", image.size, (255, 255, 255))
|
51 |
+
bg.paste(image, mask=alpha)
|
52 |
+
return bg
|
53 |
+
else:
|
54 |
+
return image
|
55 |
+
|
56 |
+
def load_image(image_file):
|
57 |
+
if image_file.startswith("http://") or image_file.startswith("https://"):
|
58 |
+
response = requests.get(image_file)
|
59 |
+
image = Image.open(BytesIO(response.content)).convert("RGB")
|
60 |
+
else:
|
61 |
+
image = Image.open(image_file).convert("RGB")
|
62 |
+
image = remove_transparency(image)
|
63 |
+
return image
|
64 |
+
|
65 |
+
def process_image(image):
|
66 |
+
args = {"image_aspect_ratio": "pad"}
|
67 |
+
image_tensor = process_images([image], image_processor, args)
|
68 |
+
return image_tensor.to(model.device, dtype=torch.float16)
|
69 |
+
|
70 |
+
def create_prompt(prompt: str):
|
71 |
+
conv = conv_templates["llava_v0"].copy()
|
72 |
+
roles = conv.roles
|
73 |
+
prompt = DEFAULT_IMAGE_TOKEN + "\n" + prompt
|
74 |
+
conv.append_message(roles[0], prompt)
|
75 |
+
conv.append_message(roles[1], None)
|
76 |
+
return conv.get_prompt(), conv
|
77 |
+
|
78 |
+
def remove_duplicates(string):
|
79 |
+
words = string.split()
|
80 |
+
unique_words = []
|
81 |
+
|
82 |
+
for word in words:
|
83 |
+
if word not in unique_words:
|
84 |
+
unique_words.append(word)
|
85 |
+
|
86 |
+
return ' '.join(unique_words)
|
87 |
+
|
88 |
+
def ask_image(image: Image, prompt: str):
|
89 |
+
image_tensor = process_image(image)
|
90 |
+
prompt, conv = create_prompt(prompt)
|
91 |
+
input_ids = (
|
92 |
+
tokenizer_image_token(
|
93 |
+
prompt,
|
94 |
+
tokenizer,
|
95 |
+
IMAGE_TOKEN_INDEX,
|
96 |
+
return_tensors="pt",
|
97 |
+
)
|
98 |
+
.unsqueeze(0)
|
99 |
+
.to(model.device)
|
100 |
+
)
|
101 |
+
|
102 |
+
stop_str = conv.sep if conv.sep_style != SeparatorStyle.TWO else conv.sep2
|
103 |
+
stopping_criteria = KeywordsStoppingCriteria(keywords=[stop_str], tokenizer=tokenizer, input_ids=input_ids)
|
104 |
+
|
105 |
+
with torch.inference_mode():
|
106 |
+
output_ids = model.generate(
|
107 |
+
input_ids,
|
108 |
+
images=image_tensor,
|
109 |
+
do_sample=True,
|
110 |
+
temperature=0.2,
|
111 |
+
max_new_tokens=2048,
|
112 |
+
use_cache=True,
|
113 |
+
stopping_criteria=[stopping_criteria],
|
114 |
+
)
|
115 |
+
generated_caption = tokenizer.decode(output_ids[0, input_ids.shape[1] :], skip_special_tokens=True).strip()
|
116 |
+
|
117 |
+
# Remove unnecessary phrases from the generated caption
|
118 |
+
unnecessary_phrases = [
|
119 |
+
"The person is a",
|
120 |
+
"The image is",
|
121 |
+
"looking directly at the camera",
|
122 |
+
"in the image",
|
123 |
+
"taking a selfie",
|
124 |
+
"posing for a picture",
|
125 |
+
"holding a cellphone",
|
126 |
+
"is wearing a pair of sunglasses",
|
127 |
+
"pulled back in a ponytail",
|
128 |
+
"with a large window in the cent",
|
129 |
+
"and there are no other people or objects in the scene.",
|
130 |
+
" and.",
|
131 |
+
"..",
|
132 |
+
" is.",
|
133 |
+
]
|
134 |
+
|
135 |
+
for phrase in unnecessary_phrases:
|
136 |
+
generated_caption = generated_caption.replace(phrase, "")
|
137 |
+
|
138 |
+
# Split the caption into sentences
|
139 |
+
sentences = generated_caption.split('. ')
|
140 |
+
|
141 |
+
# Check if the last sentence is a fragment and remove it if necessary
|
142 |
+
min_sentence_length = 3
|
143 |
+
if len(sentences) > 1:
|
144 |
+
last_sentence = sentences[-1]
|
145 |
+
if len(last_sentence.split()) <= min_sentence_length:
|
146 |
+
sentences = sentences[:-1]
|
147 |
+
|
148 |
+
# Keep only the first three sentences and append periods
|
149 |
+
sentences = [s.strip() + '.' for s in sentences[:3]]
|
150 |
+
|
151 |
+
generated_caption = ' '.join(sentences)
|
152 |
+
|
153 |
+
generated_caption = remove_duplicates(generated_caption) # Remove duplicate words
|
154 |
+
|
155 |
+
return generated_caption
|
156 |
+
|
157 |
+
|
158 |
+
def fix_generated_caption(generated_caption):
|
159 |
+
# Remove unnecessary phrases from the generated caption
|
160 |
+
unnecessary_phrases = [
|
161 |
+
"The person is",
|
162 |
+
"The image is",
|
163 |
+
"looking directly at the camera",
|
164 |
+
"in the image",
|
165 |
+
"taking a selfie",
|
166 |
+
"posing for a picture",
|
167 |
+
"holding a cellphone",
|
168 |
+
"is wearing a pair of sunglasses",
|
169 |
+
"pulled back in a ponytail",
|
170 |
+
"with a large window in the cent",
|
171 |
+
"and there are no other people or objects in the scene.",
|
172 |
+
" and.",
|
173 |
+
"..",
|
174 |
+
" is.",
|
175 |
+
]
|
176 |
+
|
177 |
+
for phrase in unnecessary_phrases:
|
178 |
+
generated_caption = generated_caption.replace(phrase, "")
|
179 |
+
|
180 |
+
# Split the caption into sentences
|
181 |
+
sentences = generated_caption.split('. ')
|
182 |
+
|
183 |
+
# Check if the last sentence is a fragment and remove it if necessary
|
184 |
+
min_sentence_length = 3
|
185 |
+
if len(sentences) > 1:
|
186 |
+
last_sentence = sentences[-1]
|
187 |
+
if len(last_sentence.split()) <= min_sentence_length:
|
188 |
+
sentences = sentences[:-1]
|
189 |
+
|
190 |
+
# Capitalize the first letter of the caption and add "a" at the beginning
|
191 |
+
sentences[0] = sentences[0].strip().capitalize()
|
192 |
+
sentences[0] = "a " + sentences[0] if not sentences[0].startswith("A ") else sentences[0]
|
193 |
+
|
194 |
+
generated_caption = '. '.join(sentences)
|
195 |
+
|
196 |
+
generated_caption = remove_duplicates(generated_caption) # Remove duplicate words
|
197 |
+
|
198 |
+
return generated_caption
|
199 |
+
|
200 |
+
def find_image_urls(data, url_pattern=re.compile(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+\.(?:jpg|jpeg|png|webp)')):
|
201 |
+
"""
|
202 |
+
Recursively search for image URLs in a JSON object.
|
203 |
+
"""
|
204 |
+
if isinstance(data, list):
|
205 |
+
for item in data:
|
206 |
+
for url in find_image_urls(item, url_pattern):
|
207 |
+
yield url
|
208 |
+
elif isinstance(data, dict):
|
209 |
+
for value in data.values():
|
210 |
+
for url in find_image_urls(value, url_pattern):
|
211 |
+
yield url
|
212 |
+
elif isinstance(data, str) and url_pattern.match(data):
|
213 |
+
yield data
|
214 |
+
|
215 |
+
def gradio_interface(directory_path, prompt, exist):
|
216 |
+
image_paths = [os.path.join(directory_path, f) for f in os.listdir(directory_path) if f.endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
217 |
+
captions = []
|
218 |
+
|
219 |
+
# Check for images.json and process it
|
220 |
+
json_path = os.path.join(directory_path, 'images.json')
|
221 |
+
if os.path.exists(json_path):
|
222 |
+
with open(json_path, 'r') as json_file:
|
223 |
+
data = json.load(json_file)
|
224 |
+
image_urls = list(find_image_urls(data))
|
225 |
+
for url in image_urls:
|
226 |
+
try:
|
227 |
+
# Generate a unique filename for each image with the correct extension
|
228 |
+
extension = get_extension_from_url(url) or '.jpg' # Default to .jpg if no extension is found
|
229 |
+
unique_filename = str(uuid.uuid4()) + extension
|
230 |
+
unique_filepath = os.path.join(directory_path, unique_filename)
|
231 |
+
response = requests.get(url)
|
232 |
+
with open(unique_filepath, 'wb') as img_file:
|
233 |
+
img_file.write(response.content)
|
234 |
+
image_paths.append(unique_filepath)
|
235 |
+
except Exception as e:
|
236 |
+
captions.append((url, f"Error downloading {url}: {e}"))
|
237 |
+
|
238 |
+
# Process each image path with tqdm progress tracker
|
239 |
+
for im_path in tqdm(image_paths, desc="Captioning Images", unit="image"):
|
240 |
+
base_name = os.path.splitext(os.path.basename(im_path))[0]
|
241 |
+
caption_path = os.path.join(directory_path, base_name + '.caption')
|
242 |
+
|
243 |
+
# Handling existing files
|
244 |
+
if os.path.exists(caption_path) and exist == 'skip':
|
245 |
+
captions.append((base_name, "Skipped existing caption"))
|
246 |
+
continue
|
247 |
+
elif os.path.exists(caption_path) and exist == 'add':
|
248 |
+
mode = 'a'
|
249 |
+
else:
|
250 |
+
mode = 'w'
|
251 |
+
|
252 |
+
# Image captioning
|
253 |
+
try:
|
254 |
+
im = load_image(im_path)
|
255 |
+
result = ask_image(im, prompt)
|
256 |
+
|
257 |
+
# Fix the generated caption
|
258 |
+
fixed_result = fix_generated_caption(result)
|
259 |
+
|
260 |
+
# Writing to a text file
|
261 |
+
with open(caption_path, mode) as file:
|
262 |
+
if mode == 'a':
|
263 |
+
file.write("\n")
|
264 |
+
file.write(fixed_result) # Write the fixed caption
|
265 |
+
|
266 |
+
captions.append((base_name, fixed_result))
|
267 |
+
except Exception as e:
|
268 |
+
captions.append((base_name, f"Error processing {im_path}: {e}"))
|
269 |
+
|
270 |
+
return captions
|
271 |
+
|
272 |
+
iface = gr.Interface(
|
273 |
+
fn=gradio_interface,
|
274 |
+
inputs=[
|
275 |
+
Textbox(label="Directory Path"),
|
276 |
+
Textbox(default="Describe the persons, The person is appearance like eyes color, hair color, skin color, and the clothes, object position the scene and the situation. Please describe it detailed. Don't explain the artstyle of the image", label="Captioning Prompt"),
|
277 |
+
Radio(["skip", "replace", "add"], label="Existing Caption Action", default="skip")
|
278 |
+
],
|
279 |
+
outputs=[
|
280 |
+
Dataframe(type="pandas", headers=["Image", "Caption"], label="Captions")
|
281 |
+
],
|
282 |
+
title="Image Captioning",
|
283 |
+
description="Generate captions for images in a specified directory."
|
284 |
+
)
|
285 |
+
|
286 |
+
# Run the Gradio app
|
287 |
+
if __name__ == "__main__":
|
288 |
+
iface.launch()
|