#!/usr/bin/env python
|
|
|
|
from PIL import Image, ImageOps
|
|
import wave, math, array, argparse, sys, timeit
|
|
|
|
def parser():
|
|
parser = argparse.ArgumentParser()
|
|
|
|
# Specified command options and its helps message
|
|
|
|
parser.add_argument ("INPUT_FILE", help="Name of the image to be converted.")
|
|
parser.add_argument ("-r", "--rotate", help="Rotate image 90 degrees for more sexy spectograph looks and sound", action='store_true')
|
|
parser.add_argument ("-i", "--invert", help="Invert image colors.", action='store_true')
|
|
parser.add_argument ("-o", "--output", help="Name of the output wav file. Default value: out.wav).")
|
|
parser.add_argument ("-b", "--bottom", help="Bottom frequency range. Default value: 200.", type=int)
|
|
parser.add_argument ("-t", "--top", help="Top frequency range. Default value: 20000.", type=int)
|
|
parser.add_argument ("-p", "--pixels", help="Pixels per second. Default value: 30.", type=int)
|
|
parser.add_argument ("-s", "--sampling", help="Sample rate. Default value: 44100.", type=int)
|
|
|
|
# Create "arguments" object based on user input
|
|
arguments = parser.parse_args()
|
|
|
|
# Default values
|
|
|
|
min_freq = 200
|
|
max_freq = 20000
|
|
sample_rate = 44100
|
|
pixels = 30
|
|
output = "out.wav"
|
|
rotate = False
|
|
invert = False
|
|
|
|
# Check arguments values
|
|
if arguments.output:
|
|
output = arguments.output
|
|
if arguments.bottom:
|
|
min_freq = arguments.bottom
|
|
if arguments.top:
|
|
max_freq = arguments.top
|
|
if arguments.pixels:
|
|
pixels = arguments.pixels
|
|
if arguments.sampling:
|
|
sample_rate = arguments.sampling
|
|
if arguments.rotate:
|
|
rotate = True
|
|
if arguments.invert:
|
|
invert = True
|
|
|
|
print ("------------------------------------ Simple Image To Sound Script ------------------------------------\n")
|
|
print ('Input file: %s.' % arguments.INPUT_FILE)
|
|
print ('Frequency range: %d - %d.' % (min_freq, max_freq))
|
|
print ('Pixels per second: %d.' % pixels)
|
|
print ('Sampling rate: %d.' % sample_rate)
|
|
print ('Rotate Image: %s.' % ('Yes' if rotate else 'No'))
|
|
|
|
return (arguments.INPUT_FILE, output, min_freq, max_freq, pixels, sample_rate, rotate, invert)
|
|
|
|
def convert (INPUT_FILE, output, min_freq, max_freq, pixels, sample_rate, rotate, invert):
|
|
image = Image.open (INPUT_FILE).convert ('L')
|
|
|
|
# rotate image if requested
|
|
if rotate:
|
|
image = image.rotate (90)
|
|
|
|
# invert image if requested
|
|
if invert:
|
|
image = ImageOps.invert (image)
|
|
|
|
output = wave.open (output, 'w')
|
|
output.setparams ((1, 2, sample_rate, 0, 'NONE', 'not compressed'))
|
|
|
|
freq_range = max_freq - min_freq
|
|
interval = freq_range / image.size[1]
|
|
|
|
samples = sample_rate // pixels
|
|
data = array.array ('h')
|
|
|
|
# Converting process start
|
|
time_start = timeit.default_timer()
|
|
|
|
for x in range (image.size[0]):
|
|
row = []
|
|
for y in range (image.size[1]):
|
|
yinv = image.size[1] - y - 1
|
|
amplitude = image.getpixel ((x,y))
|
|
if (amplitude > 0):
|
|
row.append (gen_wave (yinv * interval + min_freq, amplitude, samples, sample_rate))
|
|
|
|
for i in range(samples):
|
|
for j in row:
|
|
try:
|
|
data[i + x * samples] += j[i]
|
|
except (IndexError):
|
|
data.insert (i + x * samples, j[i])
|
|
except (OverflowError):
|
|
if j[i] > 0:
|
|
data[i + x * samples] = 32767
|
|
else:
|
|
data[i + x * samples] = -32768
|
|
|
|
sys.stdout.write ("Conversion progress: %d%% \r" % (float(x) / image.size[0] * 100))
|
|
sys.stdout.flush()
|
|
|
|
output.writeframes (data.tobytes())
|
|
output.close()
|
|
|
|
# Converting process end
|
|
time_end = timeit.default_timer()
|
|
|
|
print ("Conversion progress: 100%")
|
|
print ("Success. Completed in %d seconds." % int(time_end - time_start))
|
|
|
|
def gen_wave (frequency, amplitude, samples, sample_rate):
|
|
cycles = samples * frequency / sample_rate
|
|
wave = []
|
|
for i in range(samples):
|
|
x = math.sin (float (cycles) * 2 * math.pi * i / float (samples)) * float (amplitude)
|
|
wave.append (int (math.floor (x)))
|
|
return wave
|
|
|
|
if __name__ == '__main__':
|
|
input_choice = parser()
|
|
convert (*input_choice)
|