ImageEncode-0.7

From SoundDB
Revision as of 11:01, 8 February 2011 by Admin (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

http://www.ohmpie.com/imageencode/

#!/usr/bin/perl
#Author: Evan Salazar
#------------------------------------------
#
#Convert bitmap image into audo file that
#can be seen in a spectrogram
#
#------------------------------------------
use SimpleWave;
use POSIX;
use Math::Trig;
use Image::BMP;
use Getopt::Std;
use strict;


################
#Constants used
################
#Version
my $version = 0.7;

#low frequency 
my $lowfreq = 200;
#high frequency:
my $highfreq = 20000;
#Sample rate for CD quality
#(The only frequency supported as of now)
my $samplerate = 44100;



#Parse commadn line arguments
my ($input,$output,$pixelsecond,$highamp,$inverse) = parseArgs();
print "Input: $input \nOutput: $output \n";
print "Pixel per second: $pixelsecond \nMax amplitude per sample: $highamp \n";
print "Image color inverted\n" if ($inverse);


#Open BMP
my $img = new Image::BMP(file=> $input);

#Create file to write to
open(WAVEFILE, ">$output");

#Get width and height
my $width = $img->{Width}; 
my $height = $img->{Height};

print "Image Width: $width Height: $height \n";

#scale frequency per height;
my $freqrange = $highfreq-$lowfreq;
my $interval = $freqrange / $height;
print "Frequency Interval: $interval\n";

#Calculate samples per width pixel;
my $samplespixel =  POSIX::floor($samplerate / $pixelsecond);  
print "Samples per Pixel: $samplespixel \n";


#This is the main algorithm
my @audioOut;
for (my $x=0;$x<$width;$x++) {
    #Calculate rows;
    my @audiorow;
    my $pos=0;
    for (my $y=0;$y<$height;$y++) {
    my $yinv = $height - $y - 1;
	#Get the amplitude
	my $amp =  color($img->xy_rgb($x,$y));
	if($amp > 0) {
	    #Generate a sine wave 
	    @audiorow[$pos++] = genSine( $yinv * $interval + $lowfreq ,$amp,$samplespixel,$samplerate);
	}
    }

    #Add all the samples for the column 
    for(my $i=0;$i<$samplespixel;$i++) {
	for(my $j=0;$j<@audiorow;$j++) {
	    @audioOut[$i + $x * $samplespixel] += $audiorow[$j]->[$i];
	}
    }
    #My attempt to print the progress
    #my $done = POSIX::floor($x / ($width - 1) * 100);
    #print "$done "  if (($done % 10) == 0); 
}

print "\nGenerating wave file \n";
#Write samples to wave file
print WAVEFILE SimpleWave::genWave(\@audioOut);


#-----------------------------------------
#
#Generate a sine wave for the given 
#frequency and amplitude 
#
#(This functions could be faster by using 
#a lookup table instead of calculating the
#wave every time)
#
#-----------------------------------------
sub genSine {

    my ($frequency,$amplitude,$samples,$samplerate) = @_;
    my $cycles = $samples * $frequency / $samplerate;
    
    my @audioArray;
    for(my $i=0;$i<$samples;$i++)
    {
	my $x = sin($cycles * 2 * pi * $i / $samples) * $amplitude;
	$audioArray[$i] = POSIX::floor($x);
    }

    return \@audioArray;
}

#-----------------------------------------
#
#Convert the RGB value to a amplitude
#
#-----------------------------------------
sub color {
    my ($r,$g,$b) = @_;
    my $total = $r + $g + $b;   
    #Invert the color if -I is set
    $total = 765 - $total if($inverse);
    my $amp = $total / 765 * $highamp;  

    #Ignore all intensitys under 50
    if($amp > 50) {
	return $amp;
    }
    return 0;
}


#-----------------------------------------
#
#Read arguments from the command line;
#
#-----------------------------------------
sub parseArgs {

    #Default arguments
    my $pixelsecond = 15;
    my $highamp = 300;
    my $inverse = 0;

    #Set options for reading
    my %args = ();
    getopts("i:o:p:a:hI",\%args);

    help() if($args{h}); 
    help("Need input filename") if(!$args{i}); 
    help("Need output filename") if(!$args{o}); 
    $pixelsecond = $args{p} if($args{p}); 
    $highamp = $args{a} if($args{a}); 
    $inverse = 1 if($args{I}); 

    return ($args{i},$args{o},$pixelsecond,$highamp,$inverse);
}


#-----------------------------------------
#
#Display help message for program
#
#-----------------------------------------
sub help {

    my ($message) = @_;
    print <<EOF;
Image Encode Ver $version by Evan Salazar
Encode an bitmat to a wave that can be viewed 
with a spectrogram
-----------------------------------------------------------------
Usage: imageEncode -i input.bmp -o output.wav

  -i [File]	Input bitmap (Best use small BMP arround 100x150) 
  -o [File]	Output wave (44.1khz 16bit mono)
  -p [int]	Pixels per Second (default: 10)
  -a [int]	Max amplitude per sample (default: 200) Max: 32000
  -I		Invert image color (Best for dark images)
  -h		Display this help message
-------------------------------------------------------------------
EOF
    print "Error: $message \n" if($message ne '');
    exit(1);
}


SimpleWave.pm

#!/usr/bin/perl
#Author Evan Salazar
#--------------------------------------------
#
#Generate a .wav file for 16 bit mono PCM
#
#-------------------------------------------
use strict;
package SimpleWave;

sub genWave {

#Get the reference to the data array

my ($audioData) = @_;

#This is the default sample rate
my $samplerate = 44100;
my $bits = 16;
my $samples = $#{$audioData} + 1;
my $channels = 1;


#Do Calculations for data wave headers
my $byterate = $samplerate * $channels * $bits / 8;
my $blockalign = $channels * $bits / 8;
my $filesize = $samples * ($bits/8) * $channels + 36;

#RIFF Chunk;
my $riff = pack('a4Va4','RIFF',$filesize,'WAVE');

#Format Chunk
my $format = pack('a4VvvVVvv',
	    	'fmt ',
		16,1,
		$channels,
		$samplerate,
		$byterate,
		$blockalign,
		$bits);

#Data Chunk
my $dataChunk = pack('a4V','data',$blockalign * $samples);

#Read audoData array
my $data;
for(my $i=0;$i<$samples;$i++) {
    
    $data .= pack('v',$audioData->[$i]);
}

#Return a byte string of the wave
return $riff . $format . $dataChunk. $data;
}
1;