Skip to content

Instantly share code, notes, and snippets.

@RubeRad
Created February 25, 2011 17:45
Show Gist options
  • Save RubeRad/844176 to your computer and use it in GitHub Desktop.
Save RubeRad/844176 to your computer and use it in GitHub Desktop.
Read in nrows,ncols, followed by nrows lines of ncols whitespace-separated pixel intensities. Output .tff
#! /bin/perl
# txt2tff.pl
use Getopt::Std;
$opt{x} = 1;
getopts('x:o:', \%opt);
# determine the dimensions, create an empty 2D matrix
$_ = <>;
@dims = split;
$lines = shift @dims;
$samps = shift @dims;
foreach (1..$lines) { push @img, [(0) x $samps] }
@lns = (<>);
if ($opt{x} > 1) {
@lns1 = @lns;
@lns = ();
foreach (@lns1) {
@row1 = split;
@row = ();
foreach $i (@row1) { push @row, ($i)x$opt{x} }
$ln = join " ", @row;
for (1..$opt{x}) { push @lns, $ln }
}
$lines *= $opt{x};
$samps *= $opt{x};
}
# stuff the pixel values into the matrix (and find the brightest pixel value)
foreach $l (0..$lines-1) {
$_ = $lns[$l];
@row = split;
foreach $s (0..$samps-1) {
$img[$l][$s] = shift @row;
$maxpx = $img[$l][$s] if $img[$l][$s] > $maxpx;
}
}
# Construct 174 bytes of header: 8-byte TFF header, then 2+12*12+4=150 bytes of IFD,
# then 4*4=16 bytes of X/YResolution (data elements too long to just include in
# the IFD itself).
# 8-byte header: 4d4d=MM=big-endian, 2a=42=TIFF code, 8=byte to start IFD
$tiff = pack "H16", "4d4d002a00000008";
# This is the only IFD in the image. 12 entries, at 12 bytes each,
# which define our 8-bit greyscale image.
$tiff .= pack "H4", "000c"; # IFD: 12 entries
# Tag.
# | |Type
# | || |Count...
# | || || |Off/Val.
# | || || || |
$tiff .= pack "H24", "00fe00040000000100000000"; #NewSubfileType
$tiff .= pack "H16N", "0100000400000001", $samps; #ImageWidth
$tiff .= pack "H16N", "0101000400000001", $lines; #ImageLength
$tiff .= pack "H16nn", "0102000300000001", 8, 0; #BitsPerSample (8)
$tiff .= pack "H16nn", "0103000300000001", 1, 0; #Compression (none)
$tiff .= pack "H16nn", "0106000300000001", 1, 0; #PhotometricInterpretation
$tiff .= pack "H16N", "0111000400000001", 174; #StripOffset (one strip, after this header)
$tiff .= pack "H16N", "0116000400000001", $lines; #RowsPerStrip (all)
$tiff .= pack "H16N", "0117000400000001", $lines*$samps; #StripByteCounts
$tiff .= pack "H16N", "011A000500000001", 158; #Offset of XResolution
$tiff .= pack "H16N", "011B000500000001", 166; #Offset of YResolution
$tiff .= pack "H16nn", "0128000300000001", 1, 0; #ResolutionUnits (unitless)
$tiff .= pack "N", 0; #offset of next IFD (never)
# At this point, $tiff is 158 bytes long, which is why XResolution
# offset is hardcoded to 158
$tiff .= pack "NN", 1, 1; #XResolution: 1:1
# At this point, $tiff is 162 bytes long, which is why YResolution
# offset is hardcoded to 162
$tiff .= pack "NN", 1, 1; #YResolution: 1:1
# The Strip (of rows of pixels)
# At this point, $tiff is 174 bytes long, which is why StripOffset
# is hardcoded to 174. All that is left is to encode each pixel as one byte
foreach $l (0..$lines-1) {
foreach $s (0..$samps-1) {
$val = int( $img[$l][$s] / $maxpx * 255 ); # rescale to 8-bit
$tiff .= pack "C", $val;
}
}
# this dumps the whole binary file
if ($opt{o}) {
open TIF, ">$opt{o}";
binmode TIF;
print TIF $tiff;
close TIF;
} else {
binmode STDOUT;
print $tiff;
}
=head1 txt2tff.pl
Convert a textual description of an image (a brick of greyscale ints) into a .tff image,
which can be viewed/converted as needed with other standard tools. The simplest possible
TFF representation is used; see http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
for details.
=head2 txt2tff.pl Typical Usage
txt2tff.pl greyvals.txt > image.tff
=head2 txt2tff.pl Command Line
The text version of the image can be specified as a command-line filename argument (as above),
or piped through STDIN, as
greyscale_generator | txt2tff.pl > image.tff
Since the script prints binary to STDOUT, you'll probably want to redirect into a file
(as above).
=head2 txt2tff.pl Inputs
The text input must begin with a header line with two positive integers that determine the
number of lines (rows) and samples (columns), respectively. There must follow
that many lines in the file, each with that many numerical greyscale values per line.
Any missing lines/samps will be construed as 0, any extra lines/samps will be ignored.
The orientation of pixels in the image will be the same as in the text input, i.e. the
first line of pixel text will be the top row of pixels in the file, beginnings of lines
in the text will be at the left of the image.
Numbers on each line may be delimited by any amount/type of whitespace. Numbers may be noninteger;
they'll all get scaled and truncated to 8-bit anyways.
=head2 txt2tff.pl Outputs
A (binary) TFF file will be printed to STDOUT. The range 0...MAX of grey values
encountered in the text input will be rescaled to 0..255. Due to the 174 bytes of
header information, the .tff file will be exactly 174+nlines*nsamps bytes long.
=head2 txt2tff.pl Changelog
=over 4
=item Apr 5 2006
Initial submission to http://etrc/sites/perl/code/share/txt2tff/
=back
=head2 txt2tff.pl Todo
Rescale minimum to 0, instead of 0-->0?
Auto-determine image dimensions without needing header line
Color?
Skip rescaling, build header first, and pack pixels into the binary file
as encountered in the input (without need for in-memory array storage)
Print binary as built (without need for in-memory $tiff storage)
=cut
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment