Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

Re: PNG to ASCII ?

by jimt (Chaplain)
on Oct 16, 2006 at 21:26 UTC ( #578595=note: print w/replies, xml ) Need Help??

in reply to PNG to ASCII ?

I would do it thusly:

#!/usr/bin/perl # call as ./ /path/to/image.png use strict; use warnings; use GD; my @master_ascii = (qw(W M 0 @ N & ), ',', qw(' - . `), ' '); my $matrix = []; my $gdimg = GD::Image->newFromPng(shift @ARGV); my ($w, $h) = $gdimg->getBounds(); my $x = 0; while ($x < $w) { my $y = 0; while ($y < $h) { my $index = $gdimg->getPixel($x, $y); my ($r,$g,$b) = $gdimg->rgb($index); $matrix->[$y]->[$x] = ($r + $g + $b) / 3; $y++; } $x++; } my ($num, $den) = (2,3); my $blocksize = $den - $num + 1; my $skipblock = $den - $blocksize; my $matrix2 = []; my $m2line = 0; my $spacer = 0; foreach my $l (0..$#$matrix) { if ($spacer > $skipblock){ $spacer = ($spacer + 1) % $den; next; } $spacer = ($spacer + 1) % $den; my $m2pix = 0; my $line = $matrix->[$l]; foreach my $p (0..$#$line) { my $total = 0; foreach my $multiplier (0..$blocksize - 1) { $total += $matrix->[$l + $multiplier]->[$p] if defined $matrix->[$l + $multiplier]->[$p]; } my $m2value = int($total / $blocksize); $matrix2->[$m2line]->[$m2pix++] = $m2value; }; $m2line++; }; $matrix = $matrix2; foreach my $m (@$matrix) { foreach my $g (@$m) { my $idx = undef; #find out the ascii value we'll use. 0 is always our black, #255 is always our white if ($g == 0){ $idx = 0; } elsif ($g == 255){ $idx = $#master_ascii; } else { $idx = @master_ascii * $g / 256; }; print $master_ascii[$idx]; } print "\n"; }

But I may have misunderstood the question. ;-)

Update: As per request by muba below, I'll explain the logic, at least roughly. This is a trimmed down version of a much more sophisticated ascii art generator I developed a few years ago. I then lost the code, I thought forever. I was sad. Then, about 2 months ago, I re-discovered it. It was in a folder called "misc" on a burned cd labeled "misc". I've been working on modernizing and cleaning it up, and when I'm done I'll probably place on CPAN. My home node pic was done with that version of the software.

Anyway, in the trimmed down version here, the logic is pretty easy. Think of a character as a bitmapped image. The text is black, the rest is white. I did this with all the printable ascii characters. From there, just arrange them from darkest -> lightest. There are something like 100 printable characters, which makes for a pretty noisy image. I've had fabulous results by scaling down to only the 5 lightest and 5 darkests. That's that @master_ascii list up at the top.

Ordering the characters is the toughest part. From there, it's easy. Use GD to read in the PNG pixel by pixel and stuff the image into an array of arrayrefs. Each offset in the array corresponds to a pixel. So $matrix->[0]->[0] is pixel 0,0 in the image file and so on (but note - the matrix is in printable order, so the coordinates are actually (y,x). At each spot, store the grayscale value of the pixel, which we get by just averaging together the RGB values.

Next, just loop through that data structure. At each point, we have a numeric value. Covert it into an offset into our @master_ascii array, and then print out that character. I think it'd work w/o that if/ladder to explicitly set the 0 and 255 values, but I had those there for a reason at some point and just left it.

Easy as pie.

Update 2: Incorporated ikegami's suggestions and re-inserted the code to help preserve aspect ratio (as I'd said, this code had been greatly simplified, so it was easy to add back in, but it's still the older stuff I haven't tidied up yet). Past experimentation yielded that a 2/3 ratio seemed to be the best. Note - I left the @master_ascii definition as is, since compressing it all into a single qw() list throws warning about the comma.

Replies are listed 'Best First'.
Re^2: PNG to ASCII ?
by ikegami (Pope) on Oct 16, 2006 at 22:13 UTC
    • elsif ($g == 255){ $idx = @master_ascii; }

      should be

      elsif ($g == 255){ $idx = $#master_ascii; }

      which allows us to change

      my $char = $master_ascii[$idx]; print defined $char ? $char : ' ';


      print $master_ascii[$idx];
    • my @master_ascii = (qw(W M 0 @ N & ), ',', qw(' - . `), ' ');

      can be simplified to

      my @master_ascii = (qw(W M 0 @ N & , ' - . `), ' ');
    • Also,

      $idx = int((scalar @master_ascii) * $g / 256);

      can be simplified to

      $idx = @master_ascii * $g / 256;

      (Multiplication already imposes a scalar context, and there's an implied int around values used as array indexes.)

    Update: It would be extra cool is you compressed the image vertically to preserve the aspect ratio.

      can be simplified to
      my @master_ascii = (qw(W M 0 @ N & , ' - . `), ' ');

      Preceded by a no warnings 'qw' to avoid the compilation-time warning Possible attempt to separate words with commas.

      David Serrano

        oh, right! I forgot about that. In this case, the following works particularly well since you can really see the progressions of ...brightness?
        my @master_ascii = split //, q(WM0@N&,'-.` );
Re^2: PNG to ASCII ?
by muba (Priest) on Oct 16, 2006 at 21:48 UTC

    I refrain from replying to the original question - has been done and I am not able to add anything anyway.

    But this is an awesome script!

    Yet, I do not fully understand what's happening. I do have some experience working with GD, it's more the logic that stumbles me. Care to explain your code?

    Update: fixed a typo. And thanks, I understand now. Nicely done, ++ there :)

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://578595]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (12)
As of 2018-03-23 13:31 GMT
Find Nodes?
    Voting Booth?
    When I think of a mole I think of:

    Results (293 votes). Check out past polls.