📄 ch29.htm
字号:
179 $$image[$i][$j]
= int($k);<BR>
180 }
<BR>
181 }<BR>
182 }</FONT></TT>
</BLOCKQUOTE>
<HR>
<H3><A NAME="ReadingtheImage"><B>Reading the Image</B></A></H3>
<P>
The format chosen for this example is the ASCII version of the
portable bitmap (PBM) file type PPM. There are two reasons for
choosing this format. First, it's simple to work with compared
to the more complicated GIF, pcX, and JPEG formats. The idea here
is to show how to use Perl to prototype algorithms, not discuss
graphics file formats. Second, the PBM utilities have some filters
and lots of conversion utilities for converting an image to a
format other than PBM if necessary. The downside of the ASCII
depiction is the slow speed in reading ASCII and the large amount
of disk space required for the image.
<P>
Obviously, after you prototype your algorithm, you'll want to
code the reading and writing of PBM files in a compiled and optimized
language such as C.
<P>
Following is the header for the image shown in Figures 29.1, 29.2,
and 29.3:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">P3<BR>
# CREATOR: XV Version 3.10 Rev: 12/16/94<BR>
99 77<BR>
255<BR>
252 252 252 252 252 252 252 252 252 252 252 252 252 252 252<BR>
252 252 252 252 252 252 252 252 252 252 252 252 252 252 252<BR>
252 252 252 252 252 252 252 252 252 252 252 252 252 252 252<BR>
252 252 252 252 252 252 252 252 252 252 252 252 252 252 252<BR>
252 252 252 252 252 252 252 252 252 252 252 252 252 252 252<BR>
252 252 252 252 252 252 252 252 252 252 252 252 252 252 252</FONT></TT>
</BLOCKQUOTE>
<P>
<TT><FONT FACE="Courier">P3</FONT></TT> is required for this image
file. The comment line concerning <TT><FONT FACE="Courier">CREATOR</FONT></TT>
is optional, but you will have to compensate for its existence.
<TT><FONT FACE="Courier">99</FONT></TT> refers to the number of
columns in the image, <TT><FONT FACE="Courier">77</FONT></TT>
refers to the number of rows of pixels, and <TT><FONT FACE="Courier">255</FONT></TT>
refers to the highest color.
<P>
What follows next is the red/green/blue (RGB) content of each
pixel in the image. All images used in this chapter have 256 gray
levels, so the RGB values are all equal. There must be 99<FONT FACE="Symbol">¥</FONT>77<FONT FACE="Symbol">¥</FONT>3
distinct RGB values.
<P>
The code for reading these pixel RGB values knows when to start
a new row by reading the number of values per row and then incrementing
the row when it reaches the columns per row. Thus, the program
reads in three values at a time and then assigns each value to
an <TT><FONT FACE="Courier">$$image</FONT></TT> array. Here's
the fragment of code to do this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">while ($a = <TEXT>) {<BR>
chop $a;<BR>
@words = split(' ',$a);<BR>
$count = $#words;<BR>
<BR>
while (@words) {<BR>
($r,$g,$b) = splice(@words,0,3);<BR>
$$image[$row][$col] = ($r+$g+$b)/3;<BR>
$col++;<BR>
if ($col >= $cols) { $row++; $col = 0 }<BR>
}<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
The RGB values in this example are all equal, but this cannot
be guaranteed because you may be working with a color image. You
take the average of the RGB intensities to determine the average
intensity of a pixel by using the following line:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$$image[$row][$col] = ($r+$g+$b)/3;</FONT></TT>
</BLOCKQUOTE>
<P>
instead of assuming grayscale images only and then using only
one value. I do take the liberty of allocating room (by default)
to 97<FONT FACE="Symbol">¥</FONT>88 because grayscale images
are used instead of color maps to get the average intensity of
a pixel.
<P>
The processing algorithm requires the dimensions of the file and
the data. These values are returned in the following line:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">return ($cols,$rows,@image);</FONT></TT>
</BLOCKQUOTE>
<H3><A NAME="GettingaHistogram"><B>Getting a Histogram</B></A>
</H3>
<P>
After the image has been read from disk into memory, you can run
some programs on it. The histogram routine to run on the image
is as follows:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">sub getHistogram {<BR>
my ($rows,$cols,$img) = @_;<BR>
my @image = @$img;<BR>
my @refered = ();<BR>
my $hst = \@refered;<BR>
<BR>
my $i,$j,$k;<BR>
<BR>
for($i=0;$i<$rows;$i++) {<BR>
for($j=0;$j<$cols;$j++) {<BR>
$k
= $$image[$i][$j];<BR>
$$hst[$k]
+= 1;<BR>
}<BR>
}<BR>
return (@refered);<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
The reference to the <TT><FONT FACE="Courier">@hst</FONT></TT>
array in the <TT><FONT FACE="Courier">getHistogram</FONT></TT>
subroutine is called to store the accumulated values per pixel
in the image.
<H2><A NAME="TheFilter"><B><FONT SIZE=5 COLOR=#FF0000>The Filter</FONT></B></A>
</H2>
<P>
A simple 3<FONT FACE="Symbol">¥</FONT>3 matrix convolution
filter is used in this section. Two filters are shown in Listing
29.5. The first filter is a uniform gain filter, and the second
is for using a type of Gaussian filter. You can modify this code
to use your own filter.
<P>
The filter is applied with a call to <TT><FONT FACE="Courier">applyFilter3</FONT></TT>.
In line 159, we pick up the <TT><FONT FACE="Courier">$convolve</FONT></TT>
filter and the pointer to the <TT><FONT FACE="Courier">$img</FONT></TT>.
The <TT><FONT FACE="Courier">convolve</FONT></TT> filter is passed
to an array with nine elements: The first three elements are in
the row above the pixel, followed by three more at the center
of row, and then three immediately below the current row. The
filter is shown at line 42 as this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">@convolve = ( 0.1, 0.1, 0.1,<BR>
0.1, 0.1, 0.1,<BR>
0.1, 0.1, 0.1);<BR>
The second filter is shown at line 49 as this:<BR>
@convolve = ( 0.1, 0.0, 0.1,<BR>
0.0, 0.5, 0.0,<BR>
0.1, 0.0, 0.1);</FONT></TT>
</BLOCKQUOTE>
<P>
The following lines are where the filter is applied and the results
are written to disk:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">print "\n Filter 2 applied";
<BR>
&applyFilter3($wd,$ht,\@convolve,\@image);<BR>
dumpImage ("filt1.ppm");</FONT></TT>
</BLOCKQUOTE>
<P>
The output of these filters is shown in Figures 29.2 and 29.3.
The way the convolution matrix is applied to the image is shown
in lines 168 to 179 in Listing 29.5.
<P>
<A HREF="f29-2.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/f29-2.gif" ><B>Figure 29.2 : </B><I>The filtered image using the first filter.</I></A>
<P>
<A HREF="f29-3.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/f29-3.gif" ><B>Figure 29.3 : </B><I>The filtered image using the second filter.</I></A>
<P>
Note that a band is left around the image so as not to overrun
the extents of the image array. When prototyping for eventual
use in a formal language, keep the restrictions of the formal
language in mind.
<P>
Finally, the image is written to disk with the <TT><FONT FACE="Courier">dumpImage</FONT></TT>
subroutine (shown in line 63). The calls to dump the image are
shown in lines 43 and 55.
<H2><A NAME="AnAddedTouch"><B><FONT SIZE=5 COLOR=#FF0000>An Added
Touch</FONT></B></A></H2>
<P>
To see the histograms in three dimensions, you can use the <TT><FONT FACE="Courier">VRML.pm</FONT></TT>
module developed earlier in this book to generate a display. By
applying different filters and then generating 3D histograms of
the resulting image, you can get a pictorial view of how each
filter affects the output. The following lines of code are for
a subroutine, <TT><FONT FACE="Courier">show3Dhistogram</FONT></TT>,
to create VRML cubes from the histogram array. Add this subroutine
to the end of the file shown in Listing 29.5:
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 use VRML;<BR>
2 use VRML::Cube;<BR>
3 use VRML::Cylinder;<BR>
4<BR>
5 sub show3Dhistogram {<BR>
6 open (VRMLFILE,">vrml1.wrl") || die "\n
Cannot open $!\n";<BR>
7 $oldfile = select VRMLFILE;<BR>
8 my $header = VRML::new();<BR>
9 $header->VRML::startHeader;<BR>
10 $header->VRML::startSeparator;<BR>
11 $width = 0.01;<BR>
12 my @cyl;<BR>
13 $hi = $#hist + 1;<BR>
14 for ($i = 0; $i < $hi; $i++) {<BR>
15 $v = $hist[$i] / 100;<BR>
16 if ($v > 0) {<BR>
17 $x = ($i * $width) % 16 ;<BR>
18 $y = ($i * $width) / 16 ;<BR>
19 $t = $header->VRML::putCube(<BR>
20 'width' =>
$width, 'height' => $v, 'depth' => $width,<BR>
21 'translation'
=> [$x,$y,0],<BR>
22 'ambientColor'
=> [$v,$v,$v]<BR>
23 );<BR>
24 }<BR>
25 }<BR>
26 $header->VRML::stopSeparator;<BR>
27 close VRMLFILE;<BR>
28 select $oldfile;<BR>
29 }</FONT></TT>
</BLOCKQUOTE>
<P>
Note that in line 7, we take care to store away the file handle
of the current output file when we set the default file handle
to <TT><FONT FACE="Courier">VRMLFILE</FONT></TT> with the <TT><FONT FACE="Courier">select</FONT></TT>
call. The <TT><FONT FACE="Courier">for</FONT></TT> loop in line
14 steps through the entire <TT><FONT FACE="Courier">hist</FONT></TT>
array and generates code to print a cube. Lines 19 through 23
are the statements for generating VRML code for a cube. Line 26
terminates the VRML output. The file handle is used to close the
VRML output file in line 27. After the VRML output file is closed,
we reset the default file handle (in line 28) to whatever it was
prior to the <TT><FONT FACE="Courier">select</FONT></TT> call.
<H2><A NAME="APartingNote"><B><FONT SIZE=5 COLOR=#FF0000>A Parting
Note</FONT></B></A></H2>
<P>
The number of routines in this chapter (and other chapters, for
that matter) make it hard for me to look them up by name. You
can use the following script to quickly get listings of all the
subroutine functions you want in a bunch of source code files.
See Listing 29.6.
<HR>
<BLOCKQUOTE>
<B>Listing 29.6. Printing the subroutines in source files.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 #!/usr/bin/perl<BR>
2 #<BR>
3 # Display all the subroutines in the files on the command
line<BR>
4 #<BR>
5 while (<>) {<BR>
6 chop;<BR>
7 if (/^\s*sub\s+(\w+(?:[:`]+))?(\w+)/)
{<BR>
8 $name = $2;<BR>
9 print $ARGV, ":Line
$. :", "$name\n";<BR>
10 close(ARGV) if eof(); #
reset line numbers<BR>
11 }<BR>
12 }</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
The key line to look at is the search pattern defined in line
7:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">/^\s*sub\s+(\w+(?:[:`]+))?(\w+)/</FONT></TT>
</BLOCKQUOTE>
<P>
Basically, the first part <TT><FONT FACE="Courier">\s*sub\s*</FONT></TT>
looks for all whitespaces (tabs and spaces) before and after the
string sub. Then it looks for an <TT><FONT FACE="Courier">(</FONT></TT>
open parenthesis, followed by a word as specified by <TT><FONT FACE="Courier">\w</FONT></TT>.
If the word that matches the <TT><FONT FACE="Courier">\w</FONT></TT>
is followed by <TT><FONT FACE="Courier">::</FONT></TT> or a single
quote <TT><FONT FACE="Courier">`</FONT></TT>, then it's considered
a class specification and accepted as such since the second question
mark <TT><FONT FACE="Courier">?</FONT></TT> allows for more than
one occurrence of such words. Note that we did not look for an
open curly brace for the subroutine code on the same line as the
subroutine declaration since the open curly brace may be on the
next line following the subroutine declaration.
<P>
The program shown in Listing 29.6 is not foolproof because it
looks for a very specific pattern in defining subroutines. However,
it will catch most subroutine definitions. The output from this
script gives you a listing of all subroutines declared in a file.
It even attempts to print subroutines in <TT><FONT FACE="Courier">*.pm</FONT></TT>
files. Actually, this script can write out a tags file by replacing
the <TT><FONT FACE="Courier">print</FONT></TT> line with the following
line:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">print "$name\t$ARGV\t\/^$_\/\n"</FONT></TT>
</BLOCKQUOTE>
<P>
Hope this will help you keep your functions in order! ;-)
<H2><A NAME="Summary"><B><FONT SIZE=5 COLOR=#FF0000>Summary</FONT></B></A>
</H2>
<P>
This chapter is designed to show you how to use the techniques
you learned in the previous chapters of this book by developing
prototyping algorithms using Perl. The built-in mathematical and
array functions of Perl can be assets when used for developing
algorithms. In this chapter we worked on a filtering algorithm
for an image, as well as reading and archiving to disk. You even
can use previously developed modules and available tools to see
how your prototypes work.
<P>
<HR WIDTH="100%"></P>
<CENTER><P><A HREF="ch28.htm" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/ch28.htm"><IMG SRC="pc.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/pc.gif" BORDER=0 HEIGHT=88 WIDTH=140></A><A HREF="#CONTENTS"><IMG SRC="cc.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/cc.gif" BORDER=0 HEIGHT=88 WIDTH=140></A><A HREF="index.htm" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/index.htm"><IMG SRC="hb.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/hb.gif" BORDER=0 HEIGHT=88 WIDTH=140></A><A HREF="ch30.htm" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/ch30.htm"><IMG
SRC="nc.gif" tppabs="http://www.mcp.com/815097600/0-672/0-672-30891-6/nc.gif" BORDER=0 HEIGHT=88 WIDTH=140></A></P></CENTER>
<P>
<HR WIDTH="100%"></P>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -