#!/usr/bin/perl #-------------------------------------------------------------------------- # This script is a modification of a pre-existing script. # The modifications are by Phillip J Rhoades. # howtophil@gmail.com # # It is based on this script: https://github.com/plurSKI/imageSpectrogram # http://www.devrand.org/2013/04/image-to-spectrogram.html by Gavin Black # # The original script created "hidden" spectrogram images. # # The modifications are for creating waterfall images in fldigi # and similar programs. Since I've made quite a few changes, I've # renamed the perl script to denote its different purpose. # # Please use waterfallpre.sh to "prep" images for transformation # into waterfall sstv images. Or use GIMP (Ph*t*sh*p, whatever). # I find gray scale images work best. Fiddle with the contrast # and brightness and make a few test runs. # # ./waterfallpre.sh "imagename.jpg" 150 # # This will generate 150-imagename.jpg # # 10 pixels wide is about 100hz in the fldigi waterfall. # So, you'll want to keep images 200ish or fewer pixels # wide in most cases. # # I'm not your dad though, so do as you like. # # After your image is ready run: # # ./imageWaterfall.pl "150-imagename.jpg" # # You will then have a wav file which will create a lossy image # in the fldigi (and similar programs) waterfall display # when received. Amaze your friends! Be the King Geek and The Dork God! # #-------------------------------------------------------------------------- use strict; use Audio::Wav; use Math::Complex; use GD; use constant TWO_PI => pi() * 2; my $wav = Audio::Wav->new(); my $sample_rate = 44100; my $bits_sample = 16; my $freq_Center = $ARGV[2] || 1500; my $y = 1; print "$freq_Center"; if( $#ARGV < 0 ) { print "Usage: $0 input_file \n"; exit 0; } # Start writing the wave with the proper arguments write_out(( $#ARGV < 1) ? "$ARGV[0].wav" : $ARGV[1], $ARGV[0]); sub write_out { # Set up the wave file my $filename = shift; my $write = $wav->write($filename, { bits_sample => $bits_sample, sample_rate => $sample_rate, channels => 1, }); # Set up the picture to read from my $imageName = shift; my $srcimage; open FILE, $imageName or die "Couldn't open: $imageName\n"; close(FILE); my $ucImage = uc $imageName; if( $ucImage =~ m/.JPG/ || $ucImage =~ m/.JPEG/ ) { $srcimage = GD::Image->newFromJpeg($imageName); } elsif ( $ucImage =~ m/.PNG/ ) { $srcimage = GD::Image->newFromPng($imageName); } elsif ( $ucImage =~ m/.GIF/ ) { $srcimage = GD::Image->newFromGif($imageName); } else { print "Unsupported filetype\nMust be png, jpg, jpeg, bmp, or gif\n"; exit -1; } my ($srcW,$srcH) = $srcimage->getBounds(); # Step through each pixel #for( my $x = 0; $x < $srcW; $x ++ ) #for( my $x = 0; $x < $srcH; $x ++ ) for( my $x = $srcH; $x > 0; $x -- ) { my @t = (); #for( my $y = 0; $y < $srcH; $y ++ ) for( my $y = $srcW; $y > 0; $y -- ) { my $index = $srcimage->getPixel($y,$x); my ($r,$g,$b) = $srcimage->rgb($index); # Set the frequency and 'color' for this pixel. Ignore if black if( $r > 10 || $g > 10 && $b > 10 ) { #my $c = 4.25 - 4.25*($r + $g + $b)/(256*3); my $c = 4.25 - 4.25*($r + $g + $b)/(256*3); if( $c < 0.75 ) { $c = 0.75; } #print "$c\n"; #push( @t, int(2000 - ( $y + 1)/( $srcH + 1) * 1800) ); #push( @t, int(2000 - ( $y + 1)/( $srcH + 1) * ( ( $srcH * 10 ) - $srcH ) ) ); #push( @t, int( ( $freq_Center + ( $srcH * 5 ) - ( $srcH / 2) ) - ( $y + 1)/( $srcH + 1) * ( ( $srcH * 10 ) - $srcH ) ) ); push( @t, int( ( $freq_Center - ( $srcW * 5 ) + ( $srcW / 2) ) + ( $y + 1)/( $srcW + 1) * ( ( $srcW * 10 ) - $srcW ) ) ); #push( @t, int(($y/$srcH)+1000) ); push( @t, $c ); } } # Ugly way to show a status percentage my $status = int(100*$x/$srcH); my $lowstatus = 100 - $status; my $statusDec = int(10000*$x/$srcH) - 100 * $status; my $lowstatusDec = 100 - $statusDec; print "\b\b\b\b\b\b\b\b\b\b\b\b$lowstatus.$lowstatusDec% "; $| = 1; # Add a .23 second set of sine waves add_sine($write, .23, @t); } # Finish up print "\b\b\b\b\b\b\b\b\b\b\b\b\b100% \n"; $write->finish(); } sub add_sine { my ($write,$length,@freqs) = @_; my $max_no = (2 ** $bits_sample) / 2; $length *= $sample_rate; for my $pos (0..$length) { my $count = 0; my $val = 0; for( $count = 0; $count < @freqs.length; $count += 2) { my $time = ($pos / $sample_rate) * @freqs[$count]; $val += sin(TWO_PI * $time)*10/(10 ** @freqs[$count + 1]); } $val /= $count+1; my $samp = $val * $max_no; $write->write($samp); } }