-
Notifications
You must be signed in to change notification settings - Fork 51
/
PLIO.hlp
1341 lines (1155 loc) · 58.9 KB
/
PLIO.hlp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.help PLIO Feb88 "Pixel List Package Design"
.ce
\fBThe IRAF Pixel List Package\fR
.ce
and
.ce
\fBIMIO Extensions to Support Image Masks\fR
.ce
Doug Tody
.ce
February, 1988
.ce
(Revised June 1988)
.NH
Introduction
The pixel list package is a general package for flagging individual pixels
or regions of an image, to mark some subset of the pixels in an image.
This may be done to flag bad pixels, or to identify those regions of an image
to be processed by some applications program. When the pixel list package is
used to flag the bad pixels in an image we call this a \fBbad pixel mask\fR,
or BPM. When used to identify the regions of an image to be processed (or
ignored), we call the list a \fBregion mask\fR.
A pixel list may be viewed conceptually as either a list or an image; the list
is merely the compressed form of a virtual \fBmask image\fR. Storing a mask
image as a list has two major advantages.
.ls
.ls [1]
Storage efficiency. Simple masks may be stored very compactly, e.g., as small
arrays stored directly in an image header, or in a separate mask file or
database.
.le
.ls [2]
Runtime efficiency. Storing a mask in list form has the advantage that one
can determine very quickly whether or not a given region of the image (e.g.,
an image line) contains any pixels in the mask. The alternative, given a
fully populated mask image (or flagging the pixels directly in the data image),
requires that one examine every pixel in the mask image, which is very
inefficient for simple masks.
.le
.le
The pixel list technique for implementing image masks is the most efficient
choice only for simple or moderately complex masks. As a mask image increases
in complexity there eventually comes a point where it would be simpler and
more efficient to use a conventional raster image to store the mask.
For example, if one wishes to associate a flag value (such as a weight) with
every pixel in an image, and the flag values vary rapidly in value across the
image, a separate image should probably be used (except that other forms of
data compression may be possible; the IRAF \fInoise function package\fR
addresses one aspect of this problem).
The pixel list related software is subdivided into two parts, the pixel list
package itself, and the extensions to IMIO to make use of the pixel list
package. The pixel list package in itself is independent of IMIO and may be
used separately.
.NH
IMIO Support for Pixel Lists
Most image applications tend to fall into two classes, the simple pointwise
transformation operators, and the more cpu intensive image analysis operations.
The simple operators usually operate upon the whole image (no mask required)
and may ignore the presence of bad pixels in the image, provided reasonable
artificial values have been provided for the bad pixels so that arithmetic
problems do not occur, and provided we keep track of the locations of the
bad pixels (by propagating the bad pixel lists), so that the bad pixel
information is available to subsequent image analysis programs.
The image analysis operators, on the other hand, must know the locations of
the bad pixels in order to exclude them from the fit. The analysis is also
often performed only upon specified regions of the image, as indicated by some
sort of image mask. Most image analysis algorithms are fairly cpu intensive,
hence as long as we can access the bad pixel list and region mask reasonably
efficiently, we would prefer a simple interface, e.g., for every buffer of
input image data read, an associated buffer the same size and dimensionality
as the input image data buffer, containing the bad pixel or region mask flag
values.
In general, if pixel lists are to be used with images, either IMIO must
provide direct support for pixel lists or the pixel list package must duplicate
much of the functionality of IMIO. This is because the pixel list must
be defined in terms of the physical image, whereas an applications
program accessing an image via IMIO may be operating upon some user specified
section of the image. To support applications transparent sectioning, boundary
extension, multiple input buffers, and so on, support for pixel lists must
be integrated into IMIO.
.NH 2
Mask Images
These considerations lead us to make it possible for a pixel list to appear
to an application as a special type of image called a \fImask image\fR, even
though the mask may be stored as a pixel list internally, and accessed or
queried directly as a list if desired. If whenever we read a block of data
from the data image, we read an equivalent block of data from the mask image,
it is evident that whatever we come up with for reading from the mask image is
going to have to look an awful lot like IMIO. Given the complexities of image
section transformations, automatic buffer allocation strategies, and so on,
the simplest solution is to just go ahead and use IMIO to access the virtual
mask image. This leads us to the interface shown in the figure below.
These routines represent an extension to IMIO to support pixel lists. The
conventional IMIO routines, used to access the mask "pixels", are not shown.
The mask image may be opened by name with \fIim_pmmap\fR, or an already opened
pixel mask, perhaps the result of some computation done by the applications
program, may be opened with \fIim_pmmapo\fR. Alternatively, if the mask is
stored (or will be stored in the case of a new mask) in a pixel list file
(".pl" extension), the mask may be opened with a conventional \fIimmap\fR call.
This last option is especially powerful, since it allows all image tasks to
access masks as if they were conventional images, e.g., one can DISPLAY or
IMCOPY a stored mask.
.ks
.nf
im = im_pmmap (maskname, mode, ref_im|NULL)
im = im_pmmapo (pm, ref_im)
imseti (im, IM_RLIO, YES|NO)
imseti (im, IM_PMDES, pm)
pm = imstati (im, IM_PMDES)
bool = im_pmlne[123] (im[, lineno[, bandno]])
bool = im_pmsne[123] (im, x1, x2[, y1, y2[, z1, z2]])
bool = im_pmlnev (im, v)
bool = im_pmsnev (im, vs, ve, ndim)
.fi
.ke
The \fImode\fR argument to \fIim_pmmap\fR specifies both the access mode for
the mask, and any standard transformations to be performed upon the mask before
i/o takes place (if an existing mask is to be read). NEW_FILE mode is used to
create a new mask; READ_ONLY to read an existing mask, and READ_WRITE to modify
an existing mask. The mode-transformation flags currently supported are
INVERT_MASK, which performs a PIX_NOT operation on the input mask, and
BOOLEAN_MASK, which converts an integer input mask to boolean. Any more
complex mask transformations or combinations must be performed explicitly
by the calling program via calls to the PMIO or PLIO routines, mapping the
resultant mask onto an image descriptor with \fIim_pmmapo\fR.
If a reference image \fIref_im\fR is specified at open time,
then the mask image will inherit any image section, boundary extension, etc.
in effect for the reference image. Inheritance occurs when the first i/o
to the mask image takes place. The reserved names "BPM" and "EMPTY",
respectively (must be upper case), denote the bad pixel mask
for the reference image, or an empty mask the size of the reference image.
If the reference image does not have a bad pixel mask, BPM and EMPTY are
equivalent. Note that separate image descriptors are used for the data
(reference) image and any mask images. Multiple mask images may be associated
with the same reference image.
Normal IMIO calls, e.g., \fIimgs2i\fR, are used to access the mask "pixels".
Since it is common for a mask to be empty over large regions of an image,
a set of boolean functions are provided for testing whether regions of the
mask are nonempty, allowing a program to avoid the expense of image mask i/o
and subseqent testing of the mask pixel values if not needed.
The boolean functions \fIim_pmlne\fR (line not empty) and \fIim_pmsne\fR
(section not empty) and their more general variants \fIim_pmlnev\fR and
\fIim_pmsnev\fR test whether the specified region of the mask image contains
any nonzero mask pixels. The calling sequences of these routines are patterned
after the corresponding IMIO pixel i/o routines, e.g., \fIim_pmlne2\fR is
intended for use with the \fIimgl2\fR routines, and \fIim_pmlnev\fR is
intended for use with the \fIimgnl\fR routines.
By default, mask image i/o operates on data buffers containing arrays of
pixels, as for a conventional data image. When i/o takes place, the interface
automatically converts between pixel format and the compressed line list format
in which the mask is stored internally. An alternative format for the mask
data is \fBrange list\fR format, enabled by setting the \fIIM_RLIO\fR parameter
to YES in an \fIimseti\fR call. Range list i/o works identically to pixel i/o,
except that the "pixel arrays" returned by IMIO (or input to IMIO) are range
list structures, as defined in \fB<plset.h>\fR and discussed in section 3.3.4.
Range list i/o at the IMIO level is fully general, i.e., the section
transformation, if any, is applied transparently to the calling program,
and the full range of IMIO i/o routines may be used.
If the data buffer is a multidimensional subraster, each line of the subraster
will be a separate range list, and the physical length in pixels of each line
of the subraster will be as for a pixel subraster.
On a read, if the length of the encoded range list exceeds the subraster
line length in pixels, and i/o error will occur. Such an i/o error cannot
occur when i/o is restricted to lines or line segments (1D buffers),
since IMIO will automatically allocate a data buffer large enough to hold
the worst case (longest) range list. Range list overflow errors are
unlikely even for subrasters, however, since the range list format is
normally much more compact than the equivalent pixel array (except for
very small subrasters or very complex masks).
.NH 2
Masked Image I/O (MIO)
The mask image i/o routines discussed in the previous section provide a
fully generalized means for directly accessing a mask as an separate entity,
independent of the associated reference data image. Two separate image
descriptors are required, and two parallel but separate sets of calls to the
IMIO routines. In many applications, however, a mask is used only to specify
those regions of the data image to be analyzed or processed. We want to
access those regions of the image visible through the mask, but are not
otherwise interested in the mask itself. The \fBmasked image i/o\fR (MIO)
interface is designed for such applications.
The MIO interface is summarized in the figure below. A named mask may be
opened on a previously opened image with the \fImio_open\fR procedure.
As for \fIim_pmopen\fR, the reserved names "BPM" and "EMPTY", respectively,
denote the bad pixel mask for the image, or an empty mask the size of the
image. If the image does not have a bad pixel mask, an empty mask will be
opened.
The \fIflags\fR argument, if nonzero, specifies an operation be performed
upon the input mask to generate the mask to be used to access the data image.
The currently supported flags are INVERT_MASK and BOOLEAN_MASK. For example,
when the mask is a bad pixel mask, INVERT_MASK would cause MIO to access only
that portion of the image which is \fInot\fR covered by the bad pixel mask,
i.e., the "good" region of the image. BOOLEAN_MASK is used to convert an
integer mask into a boolean mask. Note that in the case of inversion of an
integer mask, the PIX_NOT operation merely complements the mask pixel values,
hence will not affect the \fIregion\fR covered by the mask. To invert an
integer mask in the region sense, use INVERT_MASK+BOOLEAN_MASK.
.ks
.nf
mp = mio_open (maskname, flags, im)
mp = mio_openo (pm, im)
value = mio_stati (mp, param)
mio_seti (mp, param, value)
mio_setrange (mp, vs, ve, ndim)
n|EOF = mio_[gp]lseg[silrdx] (mp, ptr, mval, v, npix)
mio_close (mp)
.fi
.ke
More general mask transformations must be carried out by the user using the
PMIO package to directly compute the desired mask, the mapping the mask onto
an image descriptor with \fImio_openo\fR. For example, given as input a bad
pixel mask and a region mask, one could compute the mask specifying all pixels
in the region mask but not in the bad pixel mask. Any general mask may be
computed in this way.
An MIO application will not normally need to access the mask directly, but
if such access is required the mask descriptor may be obtained with a call
to \fImio_stati\fR to fetch the value of the parameter \fIP_PMDES\fR.
Similarly, the image descriptor may be queried as parameter \fIP_IMDES\fR,
and either parameter may be set with the corresponding call to \fImio_seti\fR.
Successive calls to a get or put line segment procedure, e.g.,
\fImio_glsegr\fR (get line segment real), return line segments from the data
image until all the data present in the area outlined by the mask has been
accessed, at which time EOF is returned as the function value.
Each line segment is returned along with a vector \fIv\fR specifying the
line of the image from which the segment is taken and the index of the first
pixel of the current line segment within the line, a count \fInpix\fR of the
number of pixels in the line segment, and the mask value \fImval\fR for the
current line segment (e.g., 1 for a boolean mask). Each line segment
corresponds to a region of constant nonzero value (\fImval\fR) in the mask.
Line segments are returned sequentially in storage order.
By default, the entire region of the image visible through the mask will be
accessed. To access only a portion of the image, the \fIimio_setrange\fR
procedure may be called before performing any i/o to specify the starting and
ending vector coordinates \fIvs\fR and \fIve\fR of the region to be accessed.
Multiple calls may be made on the same MIO descriptor to access multiple
regions of the data image, or to "rewind" a given region of the image.
.NH 2
Mask Image Storage
As we shall see in the discussion of the pixel list package, the pixel
list package is designed to permit a list to be stored anywhere, e.g.,
in a binary file, in a database, as an array in an image header,
or merely as a temporary array in memory. When the new image structures
become available storing the masks in the image header or in a global database
will probably be the best approach, but in the meantime the simplest solution
is to store each pixel list or mask in a small binary file. This approach has
the advantage of being independent of the particular image format used (none
of which are currently capable of storing the pixel list directly in any case).
If desired, the name of the mask file may be stored in the image header
as a simple string valued parameter. Alternatively, the mask name may be
input as a parameter when a task is run. A single mask may be associated
with several images, or several masks may be used with the same image.
The approach to be followed for a particular program is up to the programmer
or package designer.
The bad pixel mask is a special case, since it has a well defined logical
meaning for an image, unlike the region masks which are application specific.
The most straightforward approach is to use a single boolean mask for the
bad pixel list, using the optional header keyword \fIBPM\fR to store the
mask name, which should normally be the image name plus the pixel list file
extension "\fI.pl\fR". An integer BPM could also be used, but most applications
would treat it as a boolean mask, treating any pixel with a nonzero value as
a bad pixel.
Any additional information required to describe the bad pixels is application
specific, and may be stored in any of several ways, e.g., as a set of boolean
masks, in an integer mask, using flag bits or reserved values to describe each
pixel or region, in a separate fully populated weight image, or using the noise
function package. Most IRAF applications will probably use the BPM plus a
noise function, with the noise function being stored either as
a one-dimensional noise model or as a separate uncertainty image, transparently
to the application. The combination of a one-dimensional noise model plus a
compressed bad pixel list should provide an efficient and flexible solution
to the pixel variance problem for most applications.
.NH 2
Example
Open a data image and the associated mask image, and sum the pixels within
the area indicated by the mask.
.nf
include <pmset.h>
task sum = t_sum
.fi
.tp 6
.nf
# SUM -- Sum the image pixels lying within the given mask.
procedure t_sum()
char image[SZ_FNAME] # input data image
char mask[SZ_FNAME] # image mask
int npix, mval, totpix, m_flags
long v[PM_MAXDIM]
pointer im, mp, pp
real sum
bool clgetb()
real asumr()
int mio_glsegr()
pointer immap(), mio_open()
begin
call clgstr ("image", image, SZ_FNAME)
call clgstr ("mask", mask, SZ_FNAME)
m_flags = 0
if (clgetb ("invert"))
m_flags = INVERT_MASK
im = immap (image, READ_ONLY, 0)
mp = mio_open (mask, m_flags, im)
sum = 0; totpix = 0
while (mio_glsegr (mp, pp, mval, v, npix) != EOF) {
sum = sum + asumr (Memr[pp], npix)
totpix = totpix + npix
}
call mio_close (mp)
call imunmap (im)
call printf ("%d pixels, sum=%g, mean=%g\n")
call pargi (totpix)
call pargr (sum)
if (totpix > 0)
call pargr (sum / totpix)
else
call pargr (INDEF)
end
.fi
A more complex application might use the spatial information provided by
\fIv\fR and \fInpix\fR, or the flag values provided by \fImval\fR (for an
integer mask). For example, a surface fitting routine would accumulate each
line segment into a least squares matrix, using the coordinate information
provided as well as the pixel values.
.NH 2
The Pixel Mask Package (PMIO)
We have thus far discussed two quite different interfaces, i.e., the use
of IMIO to do pixel i/o to a mask mapped as a virtual mask image, and the MIO
interface, used to access the portion of a data image visible through a mask.
The next step is to define the interface used to access the mask object
directly as a \fImask\fR, independently of the use of masks for image i/o.
.nf
pm = pm_newmask (ref_im, depth)
pm = pm_open (bufptr|NULL)
pm = pm_create (naxes, axlen, depth)
pm = pm_newcopy (pm)
pm_close (pm)
pm_[sg]size (pm, naxes, alxen, depth)
pm_seti (pm, param, value)
value = pm_stati (pm, param)
pm_debug (pm, outfd, maxcol, flags)
bool = pm_empty (pm)
pm_compress (pm)
pm_clear (pm)
pm_load (pm, bufptr)
nwords = pm_save (pm, bufptr, buflen)
pm_loadf (pm, fname, title, maxch)
pm_savef (pm, fname, title, save_flags)
pm_[load|save]im (pm, imname[, save_flags])
ptr = pm_access (pm, v)
bool = pm_linenotempty (pm, v)
bool = pm_sectnotempty (pm, vs, ve, ndim)
pm[gp]l[lrp][sil] (pm, v, buf, b_depth, npix, rop)
pm_[set|get]plane (pm, v)
pm_point (pm, x, y, rop)
pm_circle (pm, x, y, r, rop)
pm_box (pm, x1,y1, x2,y2, rop)
pm_line (pm, x1,y1, x2,y2, width, rop)
pm_polygon (pm, x, y, npts, rop)
pm_rop (pm_src, vs, pm_dst, vs, vn, rop)
pm_stencil (pm_src, vs, pm_dst, vs, pm_stl, vs, vn, rop)
.fi
There are two variants on this lowest level interface, known as PMIO (pixel
mask i/o) and PLIO (pixel list i/o). These two interfaces are equivalent
with one difference: PMIO can accept a reference image and inherit the section
transformation of the reference image, allowing the mask to be accessed in
the coordinate system of the reference image, while PLIO is a stand alone
interface, implemented independently of IMIO without any ties to IMIO.
PMIO is implemented as a thin layer upon PLIO, with ties to IMIO to access
the section transformation for the reference image.
The PMIO interface is shown in the figure above.
With the exception of the additional routine \fIpm_newmask\fR, used to create
a new, empty mask the same size as a reference image, the PMIO routines are
identical to the corresponding PLIO routines except for the \fIpm\fR package
prefix, and the implied section transformation. Indeed, if no reference image
is specified or if the section transformation is unitary (the reference image
was opened without an image section), the two interfaces are identical.
Hence the discussion of PLIO in the next section will serve to document PMIO
as well.
To use PMIO, merely include \fB<pmset.h>\fR rather than \fI<plset.h>\fR,
and set the reference image with a call to \fIpm_seti\fR, e.g.,
call pm_seti (pm, P_REFIM, ref_im)
This step can be skipped if \fIpm_newmask\fR or \fIpm_newcopy\fR is used to
create a new mask, as the reference image will be inherited by the new mask.
Programs which use PMIO to access a mask may also use the low level line,
range, and pixel list routines discussed in section 3.1 (\fBpl_rangerop\fR etc.)
on lists returned by PMIO, since PMIO will have already transformed the data
into the coordinate system of the reference image.
In general, the PMIO interface should be used in preference to PLIO whenever
the mask to be accessed is logically associated with some data image or images.
The region description and logical region processing capabilities of PLIO are
very powerful in their own right, however, and PLIO should be used directly by
applications which do not consider the mask to be merely an overlay for a data
image. An example would be any application which operates upon a 2-dimensional
data structure other than an image (e.g., region filtering in the POE image
kernel).
.NH
The Pixel List Package (PLIO)
A pixel list is a way of representing an N-dimensional image matrix which
is well suited for applications where the represented image is sparse, or
consists of a moderate number of arbitrarily shaped regions. Routines are
provided for creating new lists, for writing to or reading from lists,
for storing or retrieving lists, and for performing various types of
operations upon entire lists to make new lists. The full set of routines
in the package are summarized in the figure below.
A pixel list, like an image, has a fixed dimensionality and size which is
determined at list creation time for the lifetime of the list. New lists
are created with \fIpl_create\fR, which returns a pointer to an empty list.
The \fIdepth\fR parameter specifies the depth of the list in bits, i.e.,
the number of bits per pixel, in the range 1-27. A boolean list has a depth
of 1 bit. In the current implementation the boolean mask is handled as a
degenerate case of an integer mask, i.e., an integer mask which happens to
have mask values in the range 0-1.
An existing list is accessed by opening a descriptor with \fIpl_open\fR and
loading the list into the runtime descriptor structure, either by specifying
a non-NULL buffer pointer \fIbufptr\fR, or by calling one of the \fIpl_load\fR
functions after opening a null descriptor.
.ks
.nf
pl = pl_open (bufptr|NULL)
pl = pl_create (naxes, axlen, depth)
pl = pl_newcopy (pl)
pl_close (pl)
pl_[sg]size (pl, naxes, axlen, depth)
pl_seti (pl, param, value)
value = pl_stati (pl, param)
pl_debug (pl, outfd, maxcol, flags)
bool = pl_empty (pl)
pl_compress (pl)
pl_clear (pl)
pl_load (pl, bufptr)
nwords = pl_save (pl, bufptr, buflen)
pl_loadf (pl, fname, title, maxch)
pl_savef (pl, fname, title, save_flags)
pl_[load|save]im (pl, imname[, save_flags])
ptr = pl_access (pl, v)
bool = pl_linenotempty (pl, v)
bool = pl_sectnotempty (pl, vs, ve, ndim)
pl[gp]l[lrp][sil] (pl, v, buf, b_depth, npix, rop)
pl_[set|get]plane (pl, v)
pl_point (pl, x, y, rop)
pl_circle (pl, x, y, r, rop)
pl_box (pl, x1,y1, x2,y2, rop)
pl_line (pl, x1,y1, x2,y2, width, rop)
pl_polygon (pl, x, y, npts, rop)
pl_rop (pl_src, vs, pl_dst, vs, vn, rop)
pl_stencil (pl_src, vs, pl_dst, vs, pl_stl, vs, vn, rop)
.fi
.ke
Pixel lists are stored externally as opaque binary byte arrays.
The function \fIpl_save\fR will encode a pixel list as a byte array and store
it in the indicated buffer, resizing the buffer if necessary.
If \fIbufptr\fR is NULL a new buffer will be allocated, overwriting the NULL.
The \fIpl_load\fR function performs the inverse function.
The \fIf\fR suffixed functions are provided for convenience when storing
lists in small binary files. The \fIim\fR suffixed functions create masks
out of actual data images, and vice versa. In the most general case, a list
is encoded into an array in memory and then stored away by the applications
wherever they wish, e.g., as an array parameter in an image header when the
new image structures become available.
Pixel list i/o is provided via the \fIpl[gp]l[lrp][sil]\fR family of functions,
which read, write, or edit (via the \fIrop\fR) lines or line segments of masks.
The naming convention is as follows:
.ks
.nf
pl package prefix
[gp] get or put
l line segment
[lrp] as a line list, range list, or pixel array
[sil] in an array of type short, int, or long
.fi
.ke
For example, \fIplglps\fR would get a line from a mask image as a fully
populated array of type short. For maximum efficiency, since this is a low
level interface, the data is read from or copied into a user allocated buffer,
rather than having the pixel list package control the buffer.
The functions \fIplgll[sil]\fR return the packed line list for the indicated
line or line segment. This is the copy-out form of access with the least
overhead, but requires that the application have knowledge of the internal
line list format.
Alternatively, if it is only desired to read from a list, accessing the list
in the internal format, the internal list for an image line may be directly
accessed by pointer with the \fIpl_access\fR function. This is the most
efficient form of access. The variant \fIpl_linenotempty\fR is similar except
that the NULL pointer is returned if the indicated line of the list is empty,
providing an efficient and convenient way to perform the empty test on mask
image lines.
The functions \fIplglr[sil]\fR are similar to the get line list form of access,
but instead of returning the encoded list-list in the internal format,
it returns a simple array of ranges of absolute pixel indices and associated
mask values. This is the recommended way of accessing the mask as a list
structured object, since it does not require knowledge of the internal packed
line list format. For example, to print line \fIv\fR of the mask on descriptor
\fIpl\fR as a series of range lists:
.ks
.nf
int buf[3,1024], n, i, plglri()
n = plglri (pl, v, buf, 0, axlen[1], 0)
do i = 1, n {
call printf ("range at %d, %d pixels, mask value = %o\n")
call pargi (buf[1,i]); call pargi (buf[2,i])
call pargi (buf[3,i])
}
.fi
.ke
These routines require that the depth in bits of the output line or range
list or pixel array be specified. This is necessary to avoid generating
numbers the size of the machine integer when inverting a mask (PIX_NOT),
e.g., if the mask depth is 8 bits, the complement of a 0 pixel should be 377,
not 37777777777. The depth argument may also be used to convert an integer
mask to a boolean mask, or vice versa (using the PIX_VALUE field of the
rasterop discussed in the next section). If \fIb_value\fR is zero, clipping
of the output values is disabled. This would be appropriate, for example,
when simply reading from a mask without modifying the pixel values.
New pixel lists may be created by having the application prepare the packed
line lists externally, inserting them directly into the pixel list structure
with a \fIput\fR routine. This is generally inadvisable, however, because
the packed line list format is considered internal to the PLIO package.
A better alternative is to input the mask data as a populated array or
as a range list, letting the PL package manage the internal line list.
A more convenient approach to creating or modifying masks for most applications
is to define the mask by specifying a series of include and exclude standard
region types (circles, boxes, lines, points, or polygons),
using the block of routines beginning with \fIpl_setplane\fR in the figure.
Note that these are two dimensional
operators; they are provided for convenience even though the pixel list
package can support images of any dimension (if the image has three or more
dimensions \fIpl_setplane\fR may be used to specify the plane to be operated
upon). The most general routine is \fIpl_polygon\fR, which may be used to
operate upon the pixels in the interior of any general polygon. All of the
region drawing operators will permit regions to extend beyond the boundary
of the mask, clipping as necessary at the boundary.
.NH 2
Pixel, Line, and Range List Routines
Most of the N dimensional region or mask oriented PMIO and PLIO routines
eventually result in calls to the low level pixel, line, and range rasterop
routines and format conversion routines shown in the figure below. These
routines form the functional core of the PLIO package and will often be
responsible for most of the execution time of routines which use PLIO.
There are two main classes of routines. The \fIpl_pixrop\fR, \fIpl_linerop\fR,
and \fIpl_rangerop\fR routines perform the general raster operation on pixel
arrays, line lists, and range lists. The \fIpl_linestencil\fR routine performs
the stencil rasterop operation upon line lists; currently only a list list
version is available since this is a rarely used routine. Lastly, a set of
six routines are provided for converting between any two of the three formats,
e.g., line list to range list or pixel array and vice versa, omitting the
like-to-like conversions. For example, \fIpl_p2ri\fR would convert a pixel
array to a range list, both of type integer.
.ks
.nf
pl_linerop (ll_src, xs, src_maxval,
ll_dst, ds, dst_maxval, ll_out, npix, rop)
pl_linestencil (ll_src, xs, src_maxval, ll_dst, ds, dst_maxval,
ll_stn, xs, ll_out, npix, rop)
pl_pixrop[sil] (px_src, xs, src_maxval,
px_dst, ds, dst_maxval, npix, rop)
pl_rangerop[sil] (rl_src, xs, src_maxval,
rl_dst, ds, dst_maxval, rl_out, npix, rop)
n = pl_[lrp]2[lrp][sil] (op_src, xs, op_dst, npix)
.fi
.ke
Note that these low level routines
specify the mask depth as a maximum pixel value, rather than taking the depth
in bits as the high level PMIO and PLIO routines do, and that there are no
\fI"pm_"\fR versions of these routines - the one set of routines may be used
with both PLIO and PMIO.
.NH 2
Rasterops
The argument \fIrop\fR in the circle, box, and other routines discussed
in the previous sections is called a \fBrasterop\fR, and specifies the bitwise
operation to be performed to generate the destination operand. Much of the
generality and conciseness of the pixel list package is due to the rasterop
abstraction, which is patterned after a similar construct used by the Sun
Microsystems \fISunview\fR interface to specify operations upon \fIpixrects\fR,
which are logically similar to PLIO masks.
The rasterop defines the operation to be performed to generate the destination
mask, in terms of bitwise boolean operations performed upon the input source
and destination masks. Rasterops are constructed via a series of bitwise
\fIand\fR and \fIor\fR operations, using the following macro defines:
.ks
.nf
PIX_SRC specifies the source mask
PIX_DST specifies the destination mask
PIX_NOT(op) inverts the operand mask
PIX_VALUE(value) specifies the bitplanes to be set
.fi
.ke
Examples of some of the possible operations (out of a total of 16 possible
operations) are shown below. This table is reproduced from the SunView
Pixrect Reference Manual.
.ks
.nf
\fIRasterop\fR \fIDescription\fR
PIX_SRC copy source to destination
PIX_DST no-op
PIX_SRC | PIX_DST paint (OR source to destination)
PIX_SRC & PIX_DST mask (AND of source and destination)
PIX_NOT(PIX_SRC)&PIX_DST erase (AND destination with negation
of source)
PIX_NOT(PIX_DST) invert area (negate the existing
values)
PIX_SRC ^ PIX_DST inverting paint (XOR of source and
destination)
.fi
.ke
Here, the |&^ denote the SPP \fIor\fR, \fIand\fR, and \fIxor\fR intrinsic
functions, which must be used to construct actual rasterop expressions in SPP,
e.g.:
PIX_NOT(PIX_SRC) | PIX_DST | PIX_VALUE(v)
would actually be written as
or (PIX_NOT(PIX_SRC), PIX_DST) + PIX_VALUE(v)
The following additional macros are defined to deal with the more common
cases of setting or clearing a region.
.ks
.nf
PIX_SET (PIX_SRC | PIX_NOT(PIX_SRC))
PIX_CLR (PIX_SRC & PIX_NOT(PIX_SRC))
.fi
.ke
As a simple example, consider the case of specifying a region mask as a series
of include and exclude circles and boxes. This is trivial for a boolean mask:
an include circle or box is specified by the rasterop PIX_SET, and an exclude
by PIX_CLR. In the equivalent operation upon an integer mask this would cause
any mask values which had already been set to be replaced, hence it might be
desirable to use a PIX_SRC|PIX_DST rasterop instead, to OR the bits of the new
flag values into those of any flag values already set. Similarly, when using
\fIpl_ltop\fR to unpack a line list into a pixel array, the PIX_SRC|PIX_DST
rasterop might be specified to OR the mask segment into an existing pixel
array.
General bitwise boolean operations upon masks are provided by the \fIpl_rop\fR
and \fIpl_stencil\fR operators, which operate upon entire masks or rectangular
subregions of masks. The \fIpl_rop\fR operator combines the source and
destination masks to produce a new mask; \fIpl_stencil\fR performs the same
operation, but only in the regions specified by the stencil mask,
which must be a boolean mask. The input mask may be NULL (depending upon
the rasterop specified), or the source and destination masks may be the same.
The equivalent operators for line lists, range lists, and pixel arrays are
the \fIpl_S2D\fR family of routines, where \fIS=D=[lrp]\fR for a line list,
range list, or pixel array rop. The routine \fIpl_linestencil\fR implements
the stencil operation for a line list.
As noted above, when operating upon integer masks (\fIdepth\fR > 1), the
PIX_VALUE macro is used to specify the flag value for the indicated region.
For example, the following rasterop would set all the pixels in a region to
the same value:
PIX_VALUE(value)
to OR in the new value instead of replacing any existing flag values:
PIX_VALUE(value) | PIX_DST
If a boolean mask is to be written to an integer mask, PIX_VALUE specifies
the flag value to be used for the regions set to 1 in the input boolean mask.
For example, one might wish to combine a set of 8 boolean masks to form a
single 8 bit deep integer mask, with each bit in the integer mask corresponding
to one of the input boolean masks (001 = mask 1, 002 = mask 2, 040 = mask 3,
etc.). This could be done using the rasterop shown above, incrementing
PIX_VALUE in each operation to specify the bitplane to be set.
.NH 3
Rasterop Expression Evaluation
As mentioned above, there are sixteen possible ways to combine the source
and destination masks subject to the four possible boolean operations
(\fIand\fR, \fIor\fR, \fIxor\fR, and \fInot\fR).
More precisely, although numerous bitwise expressions can be constructed, many
of these are equivalent, and there are only sixteen fundamental operations.
These are summarized in the following table.
.nf
Operation Opcode
PIX_CLR 00
PIX_SET 17
PIX_SRC 14
PIX_DST 12
PIX_NOT(PIX_SRC) 03
PIX_NOT(PIX_DST) 05
PIX_SRC & PIX_DST 10
PIX_SRC | PIX_DST 16
PIX_SRC ^ PIX_DST 06
PIX_SRC & PIX_NOT(PIX_DST) 04
PIX_SRC | PIX_NOT(PIX_DST) 15
PIX_NOT(PIX_SRC) & PIX_DST 02
PIX_NOT(PIX_SRC) | PIX_DST 13
PIX_NOT (PIX_SRC & PIX_DST) 07
PIX_NOT (PIX_SRC | PIX_DST) 01
PIX_NOT (PIX_SRC ^ PIX_DST) 11
.fi
As an example of the redundancy of arbitrary rasterop expressions,
compare the following with the equivalent expressions (same opcode)
in the table above.
.ks
.nf
PIX_NOT(PIX_SRC) & PIX_NOT(PIX_DST) 01
PIX_NOT(PIX_SRC) ^ PIX_NOT(PIX_DST) 06
PIX_NOT(PIX_SRC) | PIX_NOT(PIX_DST) 07
PIX_NOT(PIX_SRC) ^ PIX_DST 11
PIX_SRC ^ PIX_NOT(PIX_DST) 11
.fi
.ke
Any number of additional logically redundant expressions may be constructed.
The programmer should not worry about simplifying logical expressions,
but should choose instead whatever expression is clearest for their particular
application, letting the interface handle expression optimization internally.
Of the sixteen possible fundamental operations, there are four trivial
operations (\fIclr\fR, \fIset\fR, \fIsrc\fR, \fIdst\fR),
seven composite operations, and four primary operations, namely,
\fIand\fR, \fIor\fR, \fIxor\fR, and \fInot\fR (two cases of the latter).
The composite operations are all implementable as two primary
operations in sequence. PIX_NOT is implemented by actual inversion of the
list rather than as a mode flag, to avoid complications when the list is read.
.NH 3
Rasterop Encoding
The rasterop argument specifies the bitwise boolean operation to be
performed, and optionally the pixel value to be used (in the case of an
integer mask). Both are specified as a single integer value, packed as
shown in the figure below.
.ks
.nf
+--+-------------+--------+
|32|31 5|4 1|
+--+-------------+--------+
| | pixel value | opcode |
+--+----------------------+
.fi
.ke
The following is an example of a typical rasterop (note that since the pixel
value field has a reserved range of bits which is zero prior to expression
evaluation, the "+" operator is equivalent to the \fIor\fR intrinsic function).
or(PIX_SRC, PIX_NOT(PIX_DST)) + PIX_VALUE(pix_value)
Note that the width of the pixel value field in the rasterop constrains the
effective mask depth to be 27 bits or less, if full generality is desired in
rasterop operations. Masks of up to 31 bits (the minimum unsigned precision
of a \fIlong\fR) can be stored and accessed, but rasterops using PIX_VALUE
are limited to 27 bits.
.NH 2
Pixel List Data Structures
A pixel list consists of an array of pointers to the \fIline lists\fR
forming the mask. There is one pointer for each image line; if the pointer
is NULL, no mask values are set on the associated image line. N-dimensional
images are easily handled by an N-1 dimensional array of line pointers.
Each line list consists of a series of offsets and mask pixel values
(the pixel values are normally omitted for a boolean mask).
The offsets specify the number of pixels for which the mask has the same value.
This line list format has the following two significant advantages over the
alternative technique of specifying a list of ranges using absolute pixel
coordinates:
.ls
.ls [1]
A higher degree of data compression is possible. If absolute pixel
coordinates are used in the list, the list must be an array of 32 bit
integer values in order to avoid a builtin limit on the size of the image
which can be represented. By using offsets, list elements as small as
one byte are possible.
.le
.ls [2]
A list composed of offsets is invariant with respect to translation.
For example, when extracting a subraster of an image, one can extract the
corresponding segment of each line list and perhaps edit the first element,
and the remainder of the list may be used without change.
.le
.le
When describing regular regions such as boxes it will often be the case that
successive lines of the mask are equivalent, in which case multiple line list
pointers may point to the same line list. Hence, the line lists will provide
good compression of regular objects in any two dimensional plane of the mask.
This technique can be extended to higher dimensions if desirable, without
affecting the line list data structures visible to an application.
.NH 3
Line List Encoding
A good compromise between storage efficiency and efficiency of runtime
access, while keeping things simple, is achieved if we maintain the compressed
line lists as variable length arrays of type short integer (16 bits per list
element), regardless of the mask depth. A line list consists of a series of
simple \fIinstructions\fR which are executed in sequence to reconstruct a line
of the mask. Each 16 bit instruction consists of the sign bit (not used at
present), a three bit \fIopcode\fR, and twelve bits of data, i.e.:
.ks
.nf
+--+-----------+-----------------------------+
|16|15 13|12 1|
+--+-----------+-----------------------------+
| | opcode | data |
+--+-----------------------------------------+
.fi
.ke
The significance of the data depends upon the instruction. The instructions
currently implemented are summarized in the table below.
.ks
.nf
Instruction Opcode Description
ZN 00 Output N zeros
HN 04 Output N high values
PN 05 Output N-1 zeros plus one high value
SH 01 Set high value, absolute
IH,DH 02,03 Increment or decrement high value
IS,DS 06,07 Like IH-DH, plus output one high value
.fi
.ke
In order to reconstruct a mask line, the application executing these
instructions is required to keep track of two values,
the \fIcurrent high value\fR and the \fIcurrent position\fR in the output line.
The detailed operation of each instruction is as follows:
.ls 4
.ls 8 ZN
Zero the next N (=\fIdata\fR) output pixels.
.le
.ls HN
Set the next N output pixels to the current high value.
.le
.ls PN
Zero the next N-1 output pixels, and set pixel N to the current high value.
.le
.ls SH
Set the high value (absolute rather than incremental), taking the high 15 bits
from the next word in the instruction stream, and the low 12 bits from the
current data value.
.le
.ls IH,DH
Increment (IH) or decrement (DH) the current high value by the \fIdata\fR
value. The current position is not affected.
.le
.ls IS,DS
Increment (IS) or decrement (DS) the current high value by the \fIdata\fR
value, and \fIstep\fR, i.e., output one high value.
.le
.le
The high value is assumed to be set to 1 at the beginning of a line, hence
the IH,DH and IS,DS instructions are not normally needed for boolean masks.
If the length of a line segment of constant value or the difference between
two successive high values exceeds 4096 (12 bits), then multiple instructions
are required to describe the segment or intensity change.
The performance of this encoding is very good for typical masks consisting
of isolated high or low values or extended regions at the same level.
The worst case performance occurs when successive pixels have different values.
Even in this case the encoding will only require one word (16 bits) per mask
pixel, provided either the delta intensity change between pixels is usually
less than 12 bits, or the mask represents a zero floored step function of
constant height. The worst case cannot exceed npix*2 words provided the mask
depth is 24 bits or less.
.NH 3
Example: Line List Encoding
As a simple example, consider the following line of a boolean mask.
The set pixels are 1, 4, 8 through 11, and so on, shown as \fIindex list\fR
in the figure. The corresponding \fIoffset list\fR (line list encoding)
is also shown. Note that both encodings require the same amount of space,
assuming 16 bits per list element in both cases; the index list would require
twice as much space if 32 bit list elements were used.
.ks
.nf
Index list: 1, 4, 8-11, 15, 23-39 7 words
Offset list: P1 P3 Z3 H4 P4 Z7 H17 7 words
Inverted I-L: 2-3, 5-7, 12-14, 16-22 8 words
Inverted O-L: Z1 H2 Z1 H3 Z4 H3 Z1 H7 8 words
.fi
.ke
The bottom half of the figure shows the encodings for the inverted lists,