]> pere.pagekite.me Git - homepage.git/blob - mypapers/eyebot-ip/index.html
Generated.
[homepage.git] / mypapers / eyebot-ip / index.html
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
2 "http://www.w3.org/TR/REC-html40/loose.dtd">
3 <HTML>
4 <META NAME="GENERATOR" CONTENT="TtH 2.67">
5
6
7
8 <title> Eyebot image processing primitives</title>
9
10 <H1 align="center">Eyebot image processing primitives </H1>
11
12 <H3 align=center>Petter Reinholdtsen &lt; pere@td.org.uit.no &gt; </H3>
13
14 <H3 align=center>2000-04-21 </H3>
15
16 <p>
17
18 <H2> Abstract</H2>
19 This document covers image processing functions available in Eyebot
20 RoBIOS version 2.3 internal patch level g. It tries to give the
21 reader a good start to use the available primitives as effective as
22 possible. It is based on the RoBIOS source code, to make sure the
23 function descriptions are as accurate as possible.
24 <p>
25
26 <p>
27 <H2><A NAME="tth_sEc1">
28 1</A>&nbsp;&nbsp;Introduction</H2>
29
30 <p>
31 Eyebot image functions handle 4-bit gray scale images and 24 bit color
32 images. There are two defined image types, available when including
33 &#235;yebot.h":
34
35 <p>
36
37 <pre>
38 #define imagecolumns 82
39 #define imagerows 62
40 BYTE image[imagerows][imagecolumns]; /* 5084 bytes */
41 BYTE colimage[imagerows][imagecolumns][3]; /* 15252 bytes */
42 </pre>
43
44 <p>
45 The image dimensions are 80 rows and 60 columns with one border row
46 on all sides. The color images are RGB images with red as byte 0,
47 green as byte 1 and blue as byte 2.
48
49 <p>
50 The memory layout of the image types makes this the fastest way to
51 loop thru the image components:
52
53 <p>
54
55 <pre>
56 image img;
57 int row, column;
58 for (row = 1; row &lt; imagerows-1; row++)
59 for (column = 1; column &lt; imagecolumns-1; column++)
60 process_pixel(img[row][column]);
61 </pre>
62
63 <p>
64 Note that pixels coordinates go from 1 to imagerows-2 and
65 imagecolumns-2 inclusive to skip the border pixels.
66
67 <p>
68 This loop might sometimes be faster, but it process the left and
69 right border pixels as well.
70
71 <p>
72
73 <pre>
74 image img;
75 int pos;
76 for (pos = imagecolumns; pos &lt; imagecolumns*(imagerows-1); pos++)
77 process_pixel(img[0][pos]);
78 </pre>
79
80 <p>
81 The RoBIOS kernel has a number of image processing functions
82 available. I will here group them into camera, color, grey-scale and
83 LCD display functions. They are part of the Camera (CAM), Image
84 Processing (IP) and LCD module
85
86 <p>
87 In addition to the kernel functions, the libimprov library provides a
88 number of image operations.
89
90 <p>
91 <H2><A NAME="tth_sEc2">
92 2</A>&nbsp;&nbsp;Camera functions</H2>
93
94 <p>
95
96 <pre>
97 #include "eyebot.h"
98 int CAMInit (int zoom);
99 int CAMGetColFrame (colimage * buf, int convert);
100 int CAMGetFrame (image * buf);
101 int CAMMode (int mode);
102 int CAMGet (int *bright, int *off, int *cont);
103 int CAMSet (int bright, int off, int cont);
104 </pre>
105
106 <p>
107 Eyebot/RoBIOS currently supports three different cameras; Greyscale
108 QuickCam, Color QuickCam and EyeCam (color). The first Eyebots used
109 Greyscale QuickCams, while later models used Color QuickCams. The
110 latest model uses a locally designed EyeCam, as the older QuickCams
111 are no longer produced, and the specifications for the newer QuickCams
112 are impossible to get from Logitec.
113
114 <p>
115 Both QuickCam and EyeCam use the same lenses, and these lenses can
116 be replaced to change viewing-angle. Currently we have 33, 41, 43 and
117 47 degree lenses. When changing lens on the camera, make sure to get
118 the camera back to focus. Focusing is done by moving the lens back or
119 forward by turning it in the socket. The width of the EyeCam base is
120 1/6 inch.
121
122 <p>
123 The following code will grab one image from the camera:
124
125 <p>
126
127 <pre>
128 #include "eyebot.h"
129 int camversion, resval, resval2;
130 image img;
131 colimage colimg;
132
133 while (INITERROR == (camversion = CAMInit(NORMAL)))
134 OSWait(CAMWAIT);
135
136 if (NOCAM == camversion)
137 LCDPutString("No camera");
138 else if (BWCAM &lt;= camversion &amp;&amp; camversion &lt; COLCAM)
139 resval = CAMGetFrame(&amp;img);
140 else if (COLCAM &lt;= camversion &amp;&amp; camversion &lt; 0x20)
141 resval = CAMGetColFrame(&amp;colimg, 0); /* color image */
142 resval2 = CAMGetColFrame((colimage*)&amp;img, 1); /* grey image */
143
144 if (0 != resval)
145 LCDPutString("CAMGet{Col}Frame() failed");
146 </pre>
147
148 <p>
149 CAMInit() accepts parameter WIDE, NORMAL or TELE, to change the
150 zoom of the camera. This parameter is used by grey-scale QuickCam
151 only. CAMInit() might fail the first times, and the while loop make
152 sure the camera is initialized anyway. The version numbers returned
153 from CAMInit() are:
154
155 <p>
156
157 <center>
158 <TaBle border>
159 <tr><td align="right">0 </td><td>Greyscale QuickCam </td></tr>
160 <tr><td align="right">16 </td><td>Color QuickCam </td></tr>
161 <tr><td align="right">17 </td><td>EyeCam </td></tr></TaBle>
162
163 </center>
164
165 <p>
166 CAMGetFrame() returns a 80x60 4-bit grey-scale image. The bits
167 occupy the lower 4 bit of an 8-bit BYTE.
168
169 <p>
170 CAMGetColFrame() returns a 24-bit color image if the second
171 parameter is 0, and a 4-bit grey-scale image if the second parameter
172 is 1.
173
174 <p>
175 Each grab function returns as soon as the next complete image is
176 ready. The QuickCam grey scale delivers 20 frames per second. The
177 QuickCam color frame rate is normally 6.5 frames per second. The
178 EyeCam is stable on 3.7 frames per second, using &nbsp;270000 microseconds
179 to grab one frame.
180
181 <p>
182
183 <p><A NAME="tth_fIg1">
184 </A> <center>
185 <center> <img src="image0-color.png" alt="image0-color.png"><br>
186 </center>
187 <center> <img src="image1-color.png" alt="image1-color.png"><br>
188 </center>
189
190 <center>Figure 1: Two sample 24bit color images taken
191 from the same position. Left is from QuickCam with wide lens,
192 Right is from EyeCam with narrow lens. Images are 3x the original
193 size.</center>
194 </center><p>
195 <p>
196 CAMGet(), CAMSet() and CAMMode() can be used to change the current
197 camera settings. CAMMode() is used to enable or disable
198 autobrightness in the camera. Disabling autobrightness currently only
199 works with QuickCam. CAMGet() and CAMSet() is used to change the
200 brightness and color/grey adjustment parameters. They can
201 currently only be used with the QuickCam. The CAMInit() parameter is
202 only used on grey scale QuickCam.
203
204 <p>
205 <H2><A NAME="tth_sEc3">
206 3</A>&nbsp;&nbsp;Color image functions</H2>
207
208 <p>
209 Color images are 82x62 including pixel border. The pixels are 24-bit
210 RGB (3 x 8 bit), as fetched from the color camera.
211
212 <p>
213 <H3><A NAME="tth_sEc3.1">
214 3.1</A>&nbsp;&nbsp;IPColor2Grey</H3>
215
216 <p>
217
218 <pre>
219 #include "eyebot.h"
220 int IPColor2Grey (colimage *src, image *dst);
221 </pre>
222
223 <p>
224 This function converts a color image to 4-bit grey-scale images.
225 Pixels are converted using the following approximation (division by 4
226 is faster then division by 3): grey level = (R+2*G+B)/4.
227
228 <p>
229
230 <p><A NAME="tth_fIg2">
231 </A> <center>
232 <center> <img src="image0-grey.png" alt="image0-grey.png"><br>
233 </center>
234 <center> <img src="image1-grey.png" alt="image1-grey.png"><br>
235 </center>
236
237 <center>Figure 2: Converted from color to grey-scale.</center>
238 </center><p>
239 <p>
240 There is currently no way this function might fail, so it should
241 always return 0. The function uses &nbsp;9200 microseconds on the Eyebot
242 and &nbsp;350 microseconds in the simulator.
243
244 <p>
245 <H2><A NAME="tth_sEc4">
246 4</A>&nbsp;&nbsp;Grey-scale image functions</H2>
247
248 <p>
249 Grey-scale images are 82x62 including pixel border and 4-bit grey level
250 values. The lower 4 bit of the pixel BYTE are used.
251
252 <p>
253 <H3><A NAME="tth_sEc4.1">
254 4.1</A>&nbsp;&nbsp;IPDither</H3>
255
256 <p>
257
258 <center>
259 <TaBle border>
260 <tr><td align="right">Grey level </td><td>0-3 </td><td>4-6 </td><td>7-9 </td><td>10-12 </td><td>13-15 </td></tr>
261 <tr><td align="right">Pattern </td><td>
262 <TaBle>
263 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
264 00 </td></TABle></td></tr>
265 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
266 00 </td></TABle></td></tr></TaBle>
267 </td><td>
268 <TaBle>
269 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
270 00 </td></TABle></td></tr>
271 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
272 10 </td></TABle></td></tr></TaBle>
273 </td><td>
274 <TaBle>
275 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
276 01 </td></TABle></td></tr>
277 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
278 10 </td></TABle></td></tr></TaBle>
279 </td><td>
280 <TaBle>
281 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
282 01 </td></TABle></td></tr>
283 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
284 11 </td></TABle></td></tr></TaBle>
285 </td><td>
286 <TaBle>
287 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
288 11 </td></TABle></td></tr>
289 <tr><td align="center"><TablE border=0><tr><td nowrap align=center>
290 11 </td></TABle></td></tr></TaBle>
291 </td></tr></TaBle>
292
293 </center>
294
295 <p>
296
297 <pre>
298 #include "eyebot.h"
299 int IPDither (image *src, image *dst);
300 </pre>
301
302 <p>
303 Converts every second grey-scale pixel and every second row to a
304 2x2 black and white pattern. It starts in upper left corner
305 (*src)[1][1] and writes the corresponding pattern to
306 (*dst)[1-2][1-2]. The patterns are given in the table to the
307 right. The border pixels are not touched.
308
309 <p>
310
311 <p><A NAME="tth_fIg3">
312 </A> <center>
313 <center> <img src="image0-dither.png" alt="image0-dither.png"><br>
314 </center>
315 <center> <img src="image1-dither.png" alt="image1-dither.png"><br>
316 </center>
317
318 <center>Figure 3: Converted from grey-scale to 2x2 dithered.</center>
319 </center><p>
320 <p>
321 There is currently no way this function might fail, so it should
322 always return 0. The function uses &nbsp;4400 microseconds on the Eyebot
323 and &nbsp;0.31 microsecond in the simulator.
324
325 <p>
326 <H3><A NAME="tth_sEc4.2">
327 4.2</A>&nbsp;&nbsp;IPLaplace</H3>
328
329 <p>
330
331 <pre>
332 #include "eyebot.h"
333 int IPLaplace (image *src, image *dst);
334 </pre>
335
336 <p>
337 Edge detection using the Laplace operator on each pixel using a 3x3
338 pixel mask.
339
340 <p>
341
342 <br clear="all"><table border="0" width="100%"><tr><td>
343 <Table align="center"><tr><td nowrap align="center">
344 result = abs (</td><td nowrap align=center>
345 <TaBle>
346 <tr><td align="right"><TablE border=0><tr><td nowrap align=center>
347 0 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
348 <font face=symbol>-</font
349 >1 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
350 0 </td></TABle></td></tr>
351 <tr><td align="right"><TablE border=0><tr><td nowrap align=center>
352 <font face=symbol>-</font
353 >1 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
354 4 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
355 <font face=symbol>-</font
356 >1 </td></TABle></td></tr>
357 <tr><td align="right"><TablE border=0><tr><td nowrap align=center>
358 0 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
359 <font face=symbol>-</font
360 >1 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
361 0</td></TABle></td></tr></TaBle>
362 </td><td nowrap align=center>
363 )</td></Table>
364 </td><td width="1%">(1)</td></table>
365
366
367
368 <p>
369
370 <p><A NAME="tth_fIg4">
371 </A> <center>
372 <center> <img src="image0-laplace.png" alt="image0-laplace.png"><br>
373 </center>
374 <center> <img src="image1-laplace.png" alt="image1-laplace.png"><br>
375 </center>
376
377 <center>Figure 4: Result of Laplace operator.</center>
378 </center><p>
379 <p>
380 There is currently no way this function might fail, so it should
381 always return 0. This function uses &nbsp;21000 microseconds on the Eyebot
382 and &nbsp;0.11 microsecond in the simulator.
383
384 <p>
385 <H3><A NAME="tth_sEc4.3">
386 4.3</A>&nbsp;&nbsp;IPSobel</H3>
387
388 <p>
389
390 <pre>
391 #include "eyebot.h"
392 int IPSobel (image *src, image *dst);
393 </pre>
394
395 <p>
396 Edge detection using the Sobel operator on each pixel using a 3x3
397 pixel mask.
398
399 <p>
400
401 <br clear="all"><table border="0" width="100%"><tr><td>
402 <Table align="center"><tr><td nowrap align="center">
403 result = </td><td nowrap align=center>
404 <tabLe border=0><tr><td nowrap align=center>
405 abs (</td><td nowrap align=center>
406 <TaBle>
407 <tr><td align="right"><TablE border=0><tr><td nowrap align=center>
408 <font face=symbol>-</font
409 >1 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
410 0 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
411 1 </td></TABle></td></tr>
412 <tr><td align="right"><TablE border=0><tr><td nowrap align=center>
413 <font face=symbol>-</font
414 >2 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
415 0 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
416 2 </td></TABle></td></tr>
417 <tr><td align="right"><TablE border=0><tr><td nowrap align=center>
418 <font face=symbol>-</font
419 >1 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
420 0 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
421 1</td></TABle></td></tr></TaBle>
422 </td><td nowrap align=center>
423 ) + abs ( </td><td nowrap align=center>
424 <TaBle>
425 <tr><td align="right"><TablE border=0><tr><td nowrap align=center>
426 <font face=symbol>-</font
427 >1 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
428 <font face=symbol>-</font
429 >2 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
430 <font face=symbol>-</font
431 >1 </td></TABle></td></tr>
432 <tr><td align="right"><TablE border=0><tr><td nowrap align=center>
433 0 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
434 0 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
435 0 </td></TABle></td></tr>
436 <tr><td align="right"><TablE border=0><tr><td nowrap align=center>
437 1 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
438 2 </td></TABle></td><td align="right"><TablE border=0><tr><td nowrap align=center>
439 1</td></TABle></td></tr></TaBle>
440 </td><td nowrap align=center>
441 )</td></tabLe><hr NOSHADE>3<Br></td><td nowrap align=center>
442 </td></Table>
443 </td><td width="1%">(2)</td></table>
444
445
446
447 <p>
448
449 <p><A NAME="tth_fIg5">
450 </A> <center>
451 <center> <img src="image0-sobel.png" alt="image0-sobel.png"><br>
452 </center>
453 <center> <img src="image1-sobel.png" alt="image1-sobel.png"><br>
454 </center>
455
456 <center>Figure 5: Result of Sobel operator.</center>
457 </center><p>
458 <p>
459 There is currently no way this function might fail, so it should
460 always return 0. This function uses &nbsp;35000 microseconds on the Eyebot
461 and &nbsp;0.42 microsecond in the simulator.
462
463 <p>
464 <H3><A NAME="tth_sEc4.4">
465 4.4</A>&nbsp;&nbsp;IPDiffer</H3>
466
467 <p>
468
469 <pre>
470 #include "eyebot.h"
471 int IPDiffer (image *source1, image *source2, image *destination);
472 </pre>
473
474 <p>
475 Calculate the grey level difference and store result in
476 destination. The following formula is used for each pixel:
477
478 <p>
479
480 <br clear="all"><table border="0" width="100%"><tr><td>
481 <Table align="center"><tr><td nowrap align="center">
482 destination = abs ( source1 <font face=symbol>-</font
483 > source2 )</td></Table>
484 </td><td width="1%">(3)</td></table>
485
486
487
488 <p>
489
490 <p><A NAME="tth_fIg6">
491 </A> <center>
492 <center> <img src="image0-differ.png" alt="image0-differ.png"><br>
493 </center>
494 <center> <img src="image1-differ.png" alt="image1-differ.png"><br>
495 </center>
496
497 <center>Figure 6: The result of 'Sobel' minus 'Laplace' operator.</center>
498 </center><p>
499 <p>
500 There is currently no way this function might fail, so it should
501 always return 0. This function uses &nbsp;8200 microseconds on the Eyebot
502 and &nbsp;0.11 microsecond in the simulator.
503
504 <p>
505 <H2><A NAME="tth_sEc5">
506 5</A>&nbsp;&nbsp;LCD image display functions</H2>
507
508 <p>
509 The LCD can display a black and white image with geometry 128x64 or
510 convert a 82x62 grey-scale image (excluding the border pixels) to
511 black and white before displaying it in the upper left corner of the
512 LCD display.
513
514 <p>
515 <H3><A NAME="tth_sEc5.1">
516 5.1</A>&nbsp;&nbsp;LCDPutImage</H3>
517
518 <p>
519
520 <pre>
521 #include "eyebot.h"
522 int LCDPutImage (BYTE bwimg[(128/8)*64]);
523 </pre>
524
525 <p>
526 Write a 128x64 black/white image on the LCD display. Each byte is
527 8 pixel values. The bytes are printed left to right, top to
528 botton. The bits in each byte are printed in right to left order. I.e
529 the bits of the first two bytes in the array are printed in this
530 order: 76543210 76543210.
531
532 <p>
533 There is currently no way this function might fail, so it should
534 always return 0. This function uses &nbsp;5500 microseconds on the Eyebot
535 and &nbsp;20000 microsecond in the simulator.
536
537 <p>
538 <H3><A NAME="tth_sEc5.2">
539 5.2</A>&nbsp;&nbsp;LCDPutGraphic</H3>
540
541 <p>
542
543 <pre>
544 #include "eyebot.h"
545 int LCDPutGraphic (image *img);
546 </pre>
547
548 <p>
549 Converts and writes a 4-bit gray scale image in the upper left
550 corner of the black and white LCD display. The border pixels are not
551 written. If the grey level is 0-7, the LCD pixel is inverted. If it
552 is 8-15, the pixel is left as it is. If the LCD is cleared before the
553 image is displayed, this will give a simple printout of the image.
554
555 <p>
556 There is currently no way this function might fail, so it should
557 always return 0. This function uses &nbsp;13000 microseconds on the Eyebot
558 and &nbsp;20000 microsecond in the simulator.
559
560 <p>
561 <H2><A NAME="tth_sEc6">
562 6</A>&nbsp;&nbsp;Image processing speed</H2>
563
564 <p>
565 Timings are done on 35 MHz Eyebot Mk3 using RoBIOS v2.3g. The
566 simulator ran on Pentium 133 MHz MMX. ms is time in microseconds, ips
567 is iterations per second when run in a loop.
568
569 <p>
570
571 <center>
572 <TaBle border>
573 <tr><td></td><td align="right">Simulator </td><td align="right"></td><td align="right">Eyebot </td></tr>
574 <tr><td>Function name </td><td align="right">ms </td><td align="right">ips </td><td align="right">ms </td><td align="right">ips </td></tr>
575 <tr><td>CAMGetColFrame() </td><td align="right">n/a </td><td align="right">n/a </td><td align="right">270000 </td><td align="right">3.7 </td></tr>
576 <tr><td>IPColor2Grey() </td><td align="right">350 </td><td align="right">2900 </td><td align="right">9200 </td><td align="right">110 </td></tr>
577 <tr><td>IPDither() </td><td align="right">0.31 </td><td align="right">3200000 </td><td align="right">4400 </td><td align="right">230 </td></tr>
578 <tr><td>IPLaplace() </td><td align="right">0.11 </td><td align="right">9100000 </td><td align="right">21000 </td><td align="right">49 </td></tr>
579 <tr><td>IPSobel() </td><td align="right">0.42 </td><td align="right">2400000 </td><td align="right">35000 </td><td align="right">28 </td></tr>
580 <tr><td>IPDiffer() </td><td align="right">0.11 </td><td align="right">9100000 </td><td align="right">8200 </td><td align="right">120 </td></tr>
581 <tr><td>LCDPutImage() </td><td align="right">20000 </td><td align="right">50 </td><td align="right">5500 </td><td align="right">180 </td></tr>
582 <tr><td>LCDPutGraphic() </td><td align="right">35000 </td><td align="right">29 </td><td align="right">13000 </td><td align="right">76 </td></tr></TaBle>
583
584 </center>
585
586 <p>
587 <H2><A NAME="tth_sEc7">
588 7</A>&nbsp;&nbsp;References</H2>
589
590 <DL compact>
591
592 <p>
593 <dt><b></b></dt>
594 <dd><a href="http://www.ee.uwa.edu.au/~braunl/eyebot/ftp/ROBIOS/library.html">Eyebot RoBIOS library documentation</a>, T. Bräunl, K. Schmitt, T Lampart 1998. <br>
595 http://www.ee.uwa.edu.au/&nbsp;braunl/eyebot/ftp/ROBIOS/library.html
596
597 <p>
598 <dt><b></b></dt>
599 <dd><a href="http://www.ee.uwa.edu.au/~braunl/eyebot/sim/sim.html">Eyesim -
600 Eyebot simulator</a>, N. Tay, E. Nichols, G. Ong 1999. <br>
601 http://www.ee.uwa.edu.au/&nbsp;braunl/eyebot/sim/sim.html
602
603 <p>
604 <dt><b></b></dt>
605 <dd>Digital Image Processing, R. C. Gonzales and R. E. Woods,
606 Addison-Wesley 1992
607
608 <p>
609 </DL> <H2><A NAME="tth_sEc8">
610 8</A>&nbsp;&nbsp;Changelog</H2>
611
612 <p>
613
614 <DL compact><dt><b></b></dt>
615 <dd>2000-04-21 Converted base document from HTML to TeX. Correct
616 name of EyeCam from Eyebot.
617
618 <p>
619 <dt><b></b></dt>
620 <dd>2000-01-17 Removed some spelling errors and corrected the size
621 of the eyecam base.
622
623 <p>
624 <dt><b></b></dt>
625 <dd>2000-01-10 First version released to the public.
626
627 <p>
628 </DL>
629 <p><hr><small>File translated from
630 T<sub><font size="-1">E</font></sub>X
631 by <a href="http://hutchinson.belmont.ma.us/tth/">
632 T<sub><font size="-1">T</font></sub>H</a>,
633 version 2.67.<br>On 24 Apr 2000, 11:10.</small>
634 </HTML>