howtophil icon

imageWaterfall.pl

howtophil | PRO | 02/07/24 12:12:15 AM UTC (Edited) | 0 ⭐ | 9276 👁️ | Never ⏰ | [audio, perl, image, waterfall, ham, waterfall image]
Perl |

5.02 KB

|

None

|

0 👍

/

0 👎

#!/usr/bin/perl
#--------------------------------------------------------------------------
# This script is a modification of a pre-existing script.
# The modifications are by Phillip J Rhoades.
# [email protected]
#
# 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" <output_file> <sound_frequency_center>
#
# 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 <output_file> <sound_frequency_center>\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);
   }
}
 

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎