JPEG Metadata Format Specification and Usage Notes
JPEG Metadata
Abbreviated Streams
Sources of Tables
Colorspace Transformations and Conventional Markers
Thumbnail Images
Progressive Encoding
Native Metadata Format Tree Structure and Editing
Image Metadata DTD
Stream Metadata DTD
NOTE: It is important to call dispose()
on the JPEG reader and writer objects when they are no longer needed, as
they consume significant native resources which are not adequately recovered
by garbage collection. Both reader and writer call dispose()
in their finalizers, but those finalizers may not be called before the native
code has exhausted native memory.
The JPEG writer does not support replacing pixels.
JPEG Metadata
JPEG metadata consists of the data contained in marker segments in a JPEG stream. The image metadata object returned from a read describes the contents of the marker segments between theSOI
marker and
the EOI
marker for that image. The image metadata object
passed into a write determines the contents of the stream between the
SOI
marker and the EOI
marker for that image,
subject to the controls in any ImageWriteParam
.
Stream metadata is used only for tables-only images found (or to be
placed) at the beginning of a stream containing abbreviated images.
Tables-only images are not treated as images and do not consume an
image index. The stream metadata object returned from a read describes the
contents of the marker segments between the SOI
marker and
the EOI
marker for the single tables-only image at the
beginning of the stream, if one is present. If no tables-only image is
present at the front of the stream, the getStreamMetadata
method of ImageReader
returns null
. If
stream metadata is provided to the writer, a single tables-only image
containing the tables from the stream metadata object will be written at
the beginning of the stream. If the stream metadata object contains no
tables, default tables will be written. As the sole purpose of stream
metadata is for specifying tables-only images at the front of abbreviated
streams, the stream metadata argument is useful only on the
ImageWriter.prepareWriteSequence
method. It is ignored on all
other methods.
The ImageWriter.getDefaultStreamMetadata
method returns an
object containing the tables from the ImageWriteParam
argument,
if it is a JPEGImageWriteParam
and contains tables. Otherwise,
the returned object will contain default tables.
The ImageWriter.getDefaultImageMetadata
method returns a
metadata object containing no tables if the
ImageWriteParam
argument contains tables. Otherwise the
returned metadata object will contain default visually lossless tables.
Of course, only a JPEGImageWriteParam
may contain tables.
If ignoreMetadata
is set to true
when the input
is set on the reader, stream metadata will not be available, but image
metadata will.
Abbreviated Streams
Both the reader and the writer retain their tables from one operation to the next, thus permitting the use of abbreviated streams quite naturally, with a few minor restrictions:- Abbreviated streams may contain only one tables-only image, which must come first in the stream. Subsequent tables-only images will cause undefined behavior.
- Abbreviated streams must be read fully and in order. No random access
is allowed, in either direction. The same image may be read multiple
times. If a call is made with an image index that is not the same as
or one greater than the most recent call (or 0 if no calls have been
made), then an
IllegalArgumentException
is thrown.
Note that once a tables-only image has been read, it's contents is available as stream metadata from the reader until either another tables-only image is read from another stream or the reader is reset. Changing the input does not reset the stream metadata. This is useful for reading the tables from one file, then changing the input to read an abbreviated stream containing a sequence of images. The tables will be used automatically, and will remain available as "stream" metadata.
Abbreviated streams are written using the sequence methods of
ImageWriter
. Stream metadata is used to write a tables-only
image at the beginning of the stream, and the tables are set up for use, using
ImageWriter.prepareWriteSequence
. If no stream metadata is
supplied to ImageWriter.prepareWriteSequence
, then no
tables-only image is written. If stream metadata containing no tables is
supplied to ImageWriter.prepareWriteSequence
, then a tables-only
image containing default visually lossless tables is written.
Sources of Tables
Images are written with tables if tables are present in their metadata objects or without them if no tables are present in their metadata objects. If no metadata object is present then the tables are written. The tables used for compression are taken from one of the following sources, which are consulted in order:
- If there is an
ImageWriteParam
and the compression mode is set toEXPLICIT
, default tables constructed using the quality setting are used. They are written only if the metadata contains tables or if there is no metadata, but they replace the tables in the metadata. - If there is an
ImageWriteParam
and the compression mode is set toDEFAULT
, default visually lossles tables are used. They are written only if the metadata contains tables or if there is no metadata, but they replace the tables in the metadata. - Otherwise the compression mode on the
ImageWriteParam
must be MODE_COPY_FROM_METADATA
, in which case the following are used:- the tables in the image metadata, if present
- the tables in the stream metadata, if present
- the tables in the
JPEGImageWriteParam
, if present - default visually lossless tables
JPEGImageWriteParam
s only as a means of specifying tables
when no other source is available, and this can occur only when writing to an
abbreviated stream without tables using known non-standard tables for
compression.
When reading, tables in a JPEGImageReadParam
are consulted only
if tables have not been set by any previous read. Tables set from a
JPEGImageReadParam
are overridden by any tables present in the
stream being read.
Note that if no image metadata object is specified for a particular image, a default object is used, which includes default tables.
Colorspace Transformations and Conventional Markers
Colorspace transformations are controlled by the destination type for both reading and writing of images. WhenRaster
s are
read, no colorspace transformation is performed, and any destination type
is ignored. A warning is sent to any listeners if a destination type is
specified in this case. When Raster
s are written, any
destination type is used to interpret the bands. This might result in a
JFIF or Adobe header being written, or different component ids being written
to the frame and scan headers. If values present in a metadata object do not
match the destination type, the destination type is used and a warning is sent
to any listeners.
Optional ColorSpace support: Handling of PhotoYCC (YCC), PhotoYCCA (YCCA), RGBA and YCbCrA color spaces by the standard plugin, as described below, is dependent on capabilities of the libraries used to interpret the JPEG data. Thus all consequential behaviors are optional. If the support is not available when decoding, the color space will be treated as unrecognized and the appropriate default color space for the specified number of component channels may be used. When writing, an Exception may be thrown if no suitable conversion can be applied before encoding. But where the support for these color spaces is available, the behavior must be as documented.
When reading, the contents of the stream are interpreted by the usual JPEG conventions, as follows:
- If a JFIF
APP0
marker segment is present, the colorspace is known to be either grayscale or YCbCr. If anAPP2
marker segment containing an embedded ICC profile is also present, then the YCbCr is converted to RGB according to the formulas given in the JFIF spec, and the ICC profile is assumed to refer to the resulting RGB space. - If an Adobe
APP14
marker segment is present, the colorspace is determined by consulting thetransform
flag. Thetransform
flag takes one of three values:- 2 - The image is encoded as YCCK (implicitly converted from CMYK on encoding).
- 1 - The image is encoded as YCbCr (implicitly converted from RGB on encoding).
- 0 - Unknown. 3-channel images are assumed to be RGB, 4-channel images are assumed to be CMYK.
- If neither marker segment is present, the following procedure is
followed: Single-channel images are assumed to be grayscale, and
2-channel images are assumed to be grayscale with an alpha channel.
For 3- and 4-channel images, the component ids are consulted. If these
values are 1-3 for a 3-channel image, then the image is assumed to be
YCbCr. Subject to the availability of the
optional color space support
described above, if these values are 1-4 for a 4-channel image,
then the image is assumed to be YCbCrA.
If these values are > 4, they are checked
against the ASCII codes for 'R', 'G', 'B', 'A', 'C', 'c'.
- All other images are declared uninterpretable and an exception is
thrown if an attempt is made to read one as a
BufferedImage
. Such an image may be read only as aRaster
. If an image is interpretable but there is no JavaColorSpace
available corresponding to the encoded colorspace (e.g. YCbCr), thenImageReader.getRawImageType
will returnnull
.
- If a destination type is not set, then the following default
transformations take place after upsampling: YCbCr (and YCbCrA) images
are converted to RGB (and RGBA) using the conversion provided by the
underlying IJG library and either the built-in sRGB
ColorSpace
or a custom RGBColorSpace
object based on an embedded ICC profile is used to create the outputColorModel
. PhotoYCC and PhotoYCCA images are not converted. CMYK and YCCK images are currently not supported. - If a destination image or type is set, it is used as follows: If the IJG library provides an appropriate conversion, it is used. Otherwise the default library conversion is followed by a colorspace conversion in Java.
- Bands are selected AFTER any library colorspace conversion. If a subset of either source or destination bands is used, then the default library conversions are used with no further conversion in Java, regardless of any destination type.
- An exception is thrown if an attempt is made to read an image in an
unsupported jpeg colorspace as a
BufferedImage
(e.g. CMYK). Such images may be read asRaster
s. If an image colorspace is unsupported or uninterpretable, thenImageReader.getImageTypes
will return an emptyIterator
. If a subset of the raw bands are required, aRaster
must be obtained first and the bands obtained from that.
For writing, the color transformation to apply is determined as follows:
If a subset of the source bands is to be written, no color conversion is
performed. Any destination, if set, must match the number of bands that will
be written, and serves as an interpretation of the selected bands, rather than
a conversion request. This behavior is identical to that for
Raster
s. If all the bands are to be written and an image
(as opposed to a Raster
) is being written, any destination type
is ignored and a warning is sent to any listeners.
If a destination type is used and any aspect of the metadata object, if there
is one, is not compatible with that type, the destination type is used, the
metadata written is modified from that provided, and a warning is sent to
listeners. This includes the app0JFIF
and
app14Adobe
nodes. The component ids in the sof
and
sos
nodes are not modified, however, as unless a
app0JFIF
node is present, any values may be used.
When a full image is written, a destination colorspace will be chosen based on the image contents and the metadata settings, according to the following algorithm:
If no metadata object is specified, then the following defaults apply:
- Grayscale images are written with a JFIF
APP0
marker segment. Grayscale images with alpha are written with no special marker. As required by JFIF, the component ids in the frame and scan header is set to 1. - RGB images are converted to YCbCr, subsampled in the chrominance
channels by half both vertically and horizontally, and written with a
JFIF
APP0
marker segment. If theColorSpace
of the image is based on anICCProfile
(it is an instance ofICC_ColorSpace
, but is not one of the standard built-inColorSpaces
), then that profile is embedded in anAPP2
marker segment. As required by JFIF, the component ids in the frame and scan headers are set to 1, 2, and 3. - Subject to the optional library support described above, RGBA images are converted to YCbCrA, subsampled in the chrominance channels by half both vertically and horizontally, and written without any special marker segments. The component ids in the frame and scan headers are set to 1, 2, 3, and 4.
- Subject to the optional library support
described above,
PhotoYCC and YCCAimages are subsampled by half in the chrominance
channels both vertically and horizontally and written with an
Adobe
APP14
marker segment and 'Y','C', and 'c' (and 'A' if an alpha channel is present) as component ids in the frame and scan headers.
If a metadata object is specified, then the number of channels in the
frame and scan headers must always match the number of bands to be
written, or an exception is thrown. app0JFIF
and
app14Adobe
nodes may appear in the same metadata object only
if the app14Adobe
node indicates YCbCr, and the component ids
are JFIF compatible (0-2). The various image types are processed in the
following ways:
(All multi-channel images are subsampled according to the sampling factors
in the frame header node of the metadata object, regardless of color space.)
- Grayscale Images:
- If an
app0JFIF
node is present in the metadata object, a JFIFAPP0
marker segment is written. - If an
app14Adobe
node is present in the metadata object, it is checked for validity (transform
must beUNKNOWN
) and written. - If neither node is present in the metadata object, no special marker segment is written.
- If an
- Grayscale Images with an Alpha Channel:
- If an
app0JFIF
node is present in the metadata object, it is ignored and a warning is sent to listeners, as JFIF does not support 2-channel images. - If an
app14Adobe
node is present in the metadata object, it is checked for validity (transform
must beUNKNOWN
) and written. Iftransform
is notUNKNOWN
, a warning is sent to listeners and the correct transform is written. - If neither node is present in the metadata object, no special marker segment is written.
- If an
- RGB Images:
- If an
app0JFIF
node is present in the metadata object, the image is converted to YCbCr and written with a JFIFAPP0
marker segment. If theColorSpace
of the image is based on a non-standard ICC Profile, then that profile is embedded in anAPP2
marker segment. If theColorSpace
is not based on a non-standard ICC Profile, but anapp2ICC
node appears in the metadata, then anAPP2
marker segment is written with the appropriate standard profile. Note that the profile must specify an RGB color space, as the file must be JFIF compliant. - If an
app14Adobe
node is present in the metadata object, the image is converted according to the color transform setting and written with an AdobeAPP14
marker segment. Component ids are written just as they appear in the frame and scan headers. The color transform must be either YCbCr orUNKNOWN
. If it isUNKNOWN
, the image is not color converted. - If neither node is present, the component ids in the frame header are consulted. If these indicate a colorspace as described above, then the image is converted to that colorspace if possible. If the component ids do not indicate a colorspace, then the sampling factors are consulted. If the image is to be subsampled, it is converted to YCbCr first. If the image is not to be subsampled, then no conversion is applied. No special marker segmentss are written.
- If an
- RGBA images:
Subject to the optional library support
described above,
- If an
app0JFIF
node is present in the metadata object, it is ignored and a warning is sent to listeners, as JFIF does not support 4-channel images. - If an
app14Adobe
node is present in the metadata object, the image is written with an AdobeAPP14
marker segment. No colorspace conversion is performed. Component ids are written just as they appear in the frame and scan headers. The color transform must beUNKNOWN
. If it is not, a warning is sent to listeners. - If no
app14Adobe
node is present, the component ids in the frame header are consulted. If these indicate a colorspace as described above, then the image is converted to that colorspace if possible. If the component ids do not indicate a colorspace, then the sampling factors are consulted. If the image is to be subsampled, it is converted to YCbCrA. If the image is not to be subsampled, then no conversion is applied. No special marker segments are written.
- If an
- PhotoYCC Images:
Subject to the optional library support
described above,
- If an
app0JFIF
node is present in the metadata object, the image is converted to sRGB, and then to YCbCr during encoding, and a JFIFAPP0
marker segment is written. - If an
app14Adobe
node is present in the metadata object, no conversion is applied, and an AdobeAPP14
marker segment is written. The color transform must be YCC. If it is not, a warning is sent to listeners. - If neither node is present in the metadata object, no conversion is applied, and no special marker segment is written.
- If an
- PhotoYCCA Images:
Subject to the optional library support
described above,
- If an
app0JFIF
node is present in the metadata object, it is ignored and a warning is sent to listeners, as JFIF does not support 4-channel images. - If an
app14Adobe
node is present in the metadata object, no conversion is applied, and an AdobeAPP14
marker segment is written. The color transform must beUNKNOWN
. If it is not, a warning is sent to listeners. - If neither node is present in the metadata object, no conversion is applied, and no special marker segment is written.
- If an
Thumbnail Images
Thumbnails are supported by the use of JFIF and JFIF extension marker segments. Thumbnails provided on the write methods determine the thumbnails that will be included.app0JFIF
and app0JFXX
nodes present in
the metadata do not contain any thumbnail pixel data. However, the kinds of
thumbnails written depend on the contents of the metadata object, as follows.
Any thumbnail which is to be written as an indexed or RGB image and which is
larger than 255 by 255 will be clipped, not scaled, to 255 by 255. Thumbnails
written as JPEG images may be any size. A warning is sent to any listeners
whenever a thumbnail is clipped.
- If there is a single thumbnail, it is processed as follows:
- If the thumbnail image is an RGB palette image, it is processed as
follows:
- If no
app0JFXX
node is present in the metadata, or the firstapp0JFXX
node present in the metadata contains aJFIFthumbPalette
element, a palette thumbnail is written in a JFXXAPP0
marker segment. - If the first
app0JFXX
node present in the metadata contains another thumbnail form (RGB or JPEG), the palette image is expanded to RGB and the indicated thumbnail form is written.
- If no
- If the thumbnail image is an RGB image, it is processed as follows:
- If no
app0JFXX
node is present in the metadata, the thumbnail is written as part of the JFIFAPP0
marker segment. - If the first
app0JFXX
node present in the metadata contains aJFIFthumbRGB
element, an RGB thumbnail is written in a JFXXAPP0
marker segment. - If the first
app0JFXX
node present in the metadata contains aJFIFthumbJPEG
element, a JPEG thumbnail is written in a JFXXAPP0
marker segment. - If the first
app0JFXX
node present in the metadata contains aJFIFthumbPalette
element, an RGB thumbnail is written in a JFXXAPP0
marker segment and a warning is sent to any listeners.
- If no
- If the thumbnail image is a grayscale image, it is processed as
follows:
- If no
app0JFXX
node is present in the metadata, the thumbnail is expanded to RGB and written as part of the JFIFAPP0
marker segment. - If the first
app0JFXX
node present in the metadata contains aJFIFthumbRGB
element, the thumbnail is expanded to RGB and written in a separateJFXX
RGB marker segment. - If the first
app0JFXX
node present in the metadata contains aJFIFthumbJPEG
element, a JPEG thumbnail is written in a JFXXAPP0
marker segment. - If the first
app0JFXX
node present in the metadata contains aJFIFthumbPalette
element, a JPEG thumbnail is written in a JFXXAPP0
marker segment and a warning is sent to any listeners.
- If no
- Any other thumbnail image types are ignored and a warning is sent to any listeners.
- If the thumbnail image is an RGB palette image, it is processed as
follows:
- If there are multiple thumbnails, each one is processed as above, except
that no thumbnail is placed in the JFIF
APP0
segment, and theapp0JFXX
node consulted for each thumbnail is theapp0JFXX
node from the metadata that occurs in the same sequence as the thumbnail. I.e. the firstapp0JFXX
node applies to the first thumbnail, the second node to the second thumbnail, and so on. If there are fewerapp0JFXX
nodes in the metadata than thumbnails, then those thumbnails are considered to have no matchingapp0JFXX
node. An RGB thumbnail with no matchingapp0JFXX
node is written in a JFXXAPP0
marker segment. A grayscale thumbnail with no matchingapp0JFXX
node is written as a JPEG image to a JFXXAPP0
marker segment.
Note that as the only mechanism for storing thumbnails is via the JFIF or JFIF extension marker segments, only grayscale or RGB images may have thumbnails. If thumbnails are present when writing any other type of image, the thumbnails are ignored and a warning is sent to any warning listeners.
Progressive Encoding
Progressive encoding must be enabled on theImageWriteParam
passed in to a write operation, or the image will be written sequentially,
regardless of the scan headers included in the metadata object. If
progressive encoding is enabled and set to copy from metadata, then
the sequence of scan headers from the metadata is used to write the
image. If progressive encoding is enabled and set to use a default,
then the scans in the metadata are ignored and a default set of scans
is used. Progressive encoding always forces optimized Huffman tables to
be used. Any Huffman tables present in the metadata will be ignored,
and a warning will be sent to any warning listeners.
If Huffman-table optimization is requested on the ImageWriteParam
,
all Huffman tables in the metadata or in the ImageWriteParam
itself are ignored, and a warning will be sent to any warning listeners if
any such tables are present.
Native Metadata Format Tree Structure and Editing
The DTDs below describe just the trees of metadata objects actually returned by theIIOMetadata
object. They do not include nodes
corresponding to SOI
, EOI
, or RST
markers, as these parsing delimiters do not carry any meaningful metadata.
The first node is always a JPEGvariety
node. In the
javax_imageio_jpeg_image_1.0
version of the JPEG metadata
format, this node may have one child, an app0JFIF
node,
indicating that the JPEG stream contains a JFIF marker segment and related
data, or no children, indicating that the stream contains no JFIF marker.
In future versions of the JPEG metadata format, other varieties of JPEG
metadata may be supported (e.g. Exif) by defining other types of nodes
which may appear as a child of the JPEGvariety
node.
(Note that an application wishing to interpret Exif metadata given
a metadata tree structure in the javax_imageio_jpeg_image_1.0
format must check for an unknown
marker segment with a tag
indicating an APP1
marker and containing data identifying it
as an Exif marker segment. Then it may use application-specific code to
interpret the data in the marker segment. If such an application were
to encounter a metadata tree formatted according to a future version of
the JPEG metadata format, the Exif marker segment might not be
unknown
in that format - it might be structured as a
child node of the JPEGvariety
node. Thus, it is important
for an application to specify which version to use by passing the string
identifying the version to the method/constructor used to obtain an
IIOMetadata
object.)
On reading, JFXX
and app2ICC
nodes occur as
children of an app0JFIF
node.
This is true regardless of where the JFXX APP0
and
APP2
marker segments actually occur in the stream. The ordering
of nodes within the markerSequence
node corresponds to the
ordering of marker segments found in the JPEG stream.
On writing, any JFXX
and app2ICC
nodes must
occur as children of an app0JFIF
node, itself a child of a
JPEGvariety
node, which must always be the first node.
(If the stream is not to be JFIF compliant, no app0JFIF
node
should be provided, and the JPEGvariety
node should have no
children.) Any
JFIF APP0
, JFXX APP0
, and APP2
marker
segments are written first, followed by all Adobe APP14
,
APPn
, COM
and unknown segments in the
order in which their corresponding nodes appear in the
markerSequence
node, followed by DQT
(and
DHT
for non-progressive writes) marker segments, followed by the
SOF
and SOS
marker segments. For progressive writes
using metadata to control progression, the SOS
segments are used
in the order in which their corresponding nodes occur in the
markerSequence
node.
The reset
, mergeTree
and setFromTree
operations have the following semantics for the JPEG plug-in metadata object:
reset
- A call to reset
will restore the
metadata object to the same state it had immediately after creation, whether
this came about from reading a stream or by obtaining a default object from
the ImageWriter
. This is true regardless of how many times the
metadata object has been modified since creation.
mergeTree
- Native Format
The mergeTree
operation accepts valid trees conforming to
the DTD below, and merges the nodes using the following ordering rules. In
all cases, only data present in the new node is changed in a corresponding
existing node, if any. This means that nodes cannot be removed using
mergeTree
. To remove nodes, use setFromTree
. The
tree must consist of IIOMetadataNode
s.
-
app0JFIF
- If an
app0JFIF
node already exists, the contents of the new one modify the existing one. - If there is no such node, a new one is created and inserted in the appropriate position.
- If an
-
dqt
- If there already exist
dqt
nodes in the sequence, then each table in the node replaces the first table, in anydqt
node, with the same table id. - If none of the existing
dqt
nodes contain a table with the same id, then the table is added to the last existingdqt
node. - If there are no
dqt
nodes, then a new one is created and added as follows:- If there are
dht
nodes, the newdqt
node is inserted before the first one. - If there are no
dht
nodes, the newdqt
node is inserted before ansof
node, if there is one. - If there is no
sof
node, the newdqt
node is inserted before the firstsos
node, if there is one. - If there is no
sos
node, the newdqt
node is added to the end of the sequence.
- If there are
- If there already exist
-
dht
- If there already exist
dht
nodes in the sequence, then each table in the node replaces the first table, in anydht
node, with the same table class and table id. - If none of the existing
dht
nodes contain a table with the same class and id, then the table is added to the last existingdht
node. - If there are no
dht
nodes, then a new one is created and added as follows:- If there are
dqt
nodes, the newdht
node is inserted immediately following the lastdqt
node. - If there are no
dqt
nodes, the newdht
node is inserted before ansof
node, if there is one. - If there is no
sof
node, the newdht
node is inserted before the firstsos
node, if there is one. - If there is no
sos
node, the newdht
node is added to the end of the sequence.
- If there are
- If there already exist
-
dri
- If there already exists a
dri
node, the restart interval value is updated. - If there is no
dri
node, then a new one is created and added as follows:- If there is an
sof
node, the newdri
node is inserted before it. - If there is no
sof
node, the newdri
node is inserted before the firstsos
node, if there is one. - If there is no
sos
node, the newdri
node is added to the end of the sequence.
- If there is an
- If there already exists a
-
com
A newcom
node is created and inserted as follows:- If there already exist
com
nodes, the new one is inserted after the last one. - If there are no
com
nodes, the newcom
node is inserted after theapp14Adobe
node, if there is one. - If there is no
app14Adobe
node, the newcom
node is inserted at the beginning of the sequence.
- If there already exist
-
app14Adobe
- If there already exists an
app14Adobe
node, then its attributes are updated from the node. - If there is no
app14Adobe
node, then a new one is created and added as follows:- The new
app14Adobe
node is inserted after the lastunknown
node, if there are any. - If there are no
unknown
nodes, the newapp14Adobe
node is inserted at the beginning of the sequence.
- The new
- If there already exists an
-
unknown
A newunknown
node is created and added to the sequence as follows:- If there already exist
unknown
marker nodes, the new one is inserted after the last one. - If there are no
unknown
nodes, the newunknown
node is inserted before theapp14Adobe
node, if there is one. - If there is no
app14Adobe
node, the newunknown
node is inserted at the beginning of the sequence.
- If there already exist
-
sof
- If there already exists an
sof
node in the sequence, then its values are updated from the node. - If there is no
sof
node, then a new one is created and added as follows:- If there are any
sos
nodes, the newsof
node is inserted before the first one. - If there is no
sos
node, the newsof
node is added to the end of the sequence.
- If there are any
- If there already exists an
-
sos
- If there already exists a single
sos
node, then the values are updated from the node. - If there are more than one existing
sos
nodes, then anIIOInvalidTreeException
is thrown, assos
nodes cannot be merged into a set of progressive scans. - If there are no
sos
nodes, a new one is created and added to the end of the sequence.
- If there already exists a single
mergeTree
- Standard Format
The mergeTree
operation, when given a tree in the standard
format, will modify the native tree in the following ways:
-
Chroma
- TheColorSpaceType
subnode of aChroma
node may change the target colorspace of the compressed image. The selection of a new colorspace can cause a number of changes, in keeping with the algorithms described above:app0JFIF
andapp14Adobe
nodes may be added or removed, subsampling may be added or removed, component ids may be changed, andsof
andsos
nodes will be updated accordingly. If necessary, additional quantization and huffman tables are added. In the case of quantization tables, the default will be scaled to match the quality level of any existing tables. No tables are added to metadata that does not already contain tables. If the existing metadata specifies progressive encoding, then the number of channels must not change. AnyTransparency
node is also taken into account, as an explicit value ofnone
for theAlpha
subnode can cause the removal of an alpha channel, and anything other thannone
can cause the addition of an alpha channel. -
Dimension
- APixelAspectRatio
specification can cause the contents of anapp0JFIF
node to change, if there is one present, or the addition of anapp0JFIF
node containing appropriate values, if there can be one. An appropriate pair of integers is computed from the floating-point ratio for inclusion in the node. -
Text
- Each uncompressed text item is converted to acom
node and inserted according to the rules above for mergingcom
nodes.
setFromTree
- Native Format
The setFromTree
operation, when given a tree in the native
format described below, will simply replace the existing tree in its entirety
with the new one. The tree must consist of IIOMetadataNode
s.
setFromTree
- Standard Format
The setFromTree
operation, when given a tree in the standard
format, performs a reset
followed by a merge of the new tree.
Image Metadata DTD
<!DOCTYPE "javax_imageio_jpeg_image_1.0" [ <!ELEMENT "javax_imageio_jpeg_image_1.0" (JPEGvariety, markerSequence)> <!ELEMENT "JPEGvariety" (app0JFIF)> <!-- A node grouping all marker segments specific to the variety of stream being read/written (e.g. JFIF) - may be empty --> <!ELEMENT "app0JFIF" (JFXX?, app2ICC?)> <!ATTLIST "app0JFIF" "majorVersion" #CDATA "1"> <!-- The major JFIF version number --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "app0JFIF" "minorVersion" #CDATA "2"> <!-- The minor JFIF version number --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "app0JFIF" "resUnits" ("0" | "1" | "2") "0"> <!-- The resolution units for Xdensisty and Ydensity (0 = no units, just aspect ratio; 1 = dots/inch; 2 = dots/cm) --> <!ATTLIST "app0JFIF" "Xdensity" #CDATA "1"> <!-- The horizontal density or aspect ratio numerator --> <!-- Data type: Integer --> <!-- Min value: 1 (inclusive) --> <!-- Max value: 65535 (inclusive) --> <!ATTLIST "app0JFIF" "Ydensity" #CDATA "1"> <!-- The vertical density or aspect ratio denominator --> <!-- Data type: Integer --> <!-- Min value: 1 (inclusive) --> <!-- Max value: 65535 (inclusive) --> <!ATTLIST "app0JFIF" "thumbWidth" #CDATA "0"> <!-- The width of the thumbnail, or 0 if there isn't one --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "app0JFIF" "thumbHeight" #CDATA "0"> <!-- The height of the thumbnail, or 0 if there isn't one --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ELEMENT "JFXX" (app0JFXX)*> <!-- Min children: 1 --> <!ELEMENT "app0JFXX" (JFIFthumbJPEG | JFIFthumbPalette | JFIFthumbRGB)> <!-- A JFIF extension marker segment --> <!ATTLIST "app0JFXX" "extensionCode" ("16" | "17" | "19") #IMPLIED> <!-- The JFXX extension code identifying thumbnail type: (16 = JPEG, 17 = indexed, 19 = RGB --> <!ELEMENT "JFIFthumbJPEG" (markerSequence?)> <!-- A JFIF thumbnail in JPEG format (no JFIF segments permitted) --> <!ELEMENT "JFIFthumbPalette" EMPTY> <!-- A JFIF thumbnail as an RGB indexed image --> <!ATTLIST "JFIFthumbPalette" "thumbWidth" #CDATA #IMPLIED> <!-- The width of the thumbnail --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "JFIFthumbPalette" "thumbHeight" #CDATA #IMPLIED> <!-- The height of the thumbnail --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ELEMENT "JFIFthumbRGB" EMPTY> <!-- A JFIF thumbnail as an RGB image --> <!ATTLIST "JFIFthumbRGB" "thumbWidth" #CDATA #IMPLIED> <!-- The width of the thumbnail --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "JFIFthumbRGB" "thumbHeight" #CDATA #IMPLIED> <!-- The height of the thumbnail --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ELEMENT "app2ICC" EMPTY> <!-- An ICC profile APP2 marker segment --> <!-- Optional User object: java.awt.color.ICC_Profile --> <!ELEMENT "markerSequence" (dqt | dht | dri | com | unknown | app14Adobe | sof | sos)*> <!-- A node grouping all non-jfif marker segments --> <!ELEMENT "dqt" (dqtable)*> <!-- A Define Quantization Table(s) marker segment --> <!-- Min children: 1 --> <!-- Max children: 4 --> <!ELEMENT "dqtable" EMPTY> <!-- A single quantization table --> <!-- User object: javax.imageio.plugins.jpeg.JPEGQTable --> <!ATTLIST "dqtable" "elementPrecision" #CDATA "0"> <!-- The number of bits in each table element (0 = 8, 1 = 16) --> <!-- Data type: Integer --> <!ATTLIST "dqtable" "qtableId" ("0" | "1" | "2" | "3") #REQUIRED> <!ELEMENT "dht" (dhtable)*> <!-- A Define Huffman Table(s) marker segment --> <!-- Min children: 1 --> <!-- Max children: 4 --> <!ELEMENT "dhtable" EMPTY> <!-- A single Huffman table --> <!-- User object: javax.imageio.plugins.jpeg.JPEGHuffmanTable --> <!ATTLIST "dhtable" "class" ("0" | "1") #REQUIRED> <!-- Indicates whether this is a DC (0) or an AC (1) table --> <!ATTLIST "dhtable" "htableId" ("0" | "1" | "2" | "3") #REQUIRED> <!-- The table id --> <!ELEMENT "dri" EMPTY> <!-- A Define Restart Interval marker segment --> <!ATTLIST "dri" "interval" #CDATA #REQUIRED> <!-- The restart interval in MCUs --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 65535 (inclusive) --> <!ELEMENT "com" EMPTY> <!-- A Comment marker segment. The user object contains the actual bytes. --> <!-- User object: array of [B --> <!-- Min length: 1 --> <!-- Max length: 65533 --> <!ATTLIST "com" "comment" #CDATA #IMPLIED> <!-- The comment as a string (used only if user object is null) --> <!-- Data type: String --> <!ELEMENT "unknown" EMPTY> <!-- An unrecognized marker segment. The user object contains the data not including length. --> <!-- User object: array of [B --> <!-- Min length: 1 --> <!-- Max length: 65533 --> <!ATTLIST "unknown" "MarkerTag" #CDATA #REQUIRED> <!-- The tag identifying this marker segment --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ELEMENT "app14Adobe" EMPTY> <!-- An Adobe APP14 marker segment --> <!ATTLIST "app14Adobe" "version" #CDATA "100"> <!-- The version of Adobe APP14 marker segment --> <!-- Data type: Integer --> <!-- Min value: 100 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "app14Adobe" "flags0" #CDATA "0"> <!-- The flags0 variable of an APP14 marker segment --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 65535 (inclusive) --> <!ATTLIST "app14Adobe" "flags1" #CDATA "0"> <!-- The flags1 variable of an APP14 marker segment --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 65535 (inclusive) --> <!ATTLIST "app14Adobe" "transform" ("0" | "1" | "2") #REQUIRED> <!-- The color transform applied to the image (0 = Unknown, 1 = YCbCr, 2 = YCCK) --> <!ELEMENT "sof" (componentSpec)*> <!-- A Start Of Frame marker segment --> <!-- Min children: 1 --> <!-- Max children: 4 --> <!ATTLIST "sof" "process" ("0" | "1" | "2") #IMPLIED> <!-- The JPEG process (0 = Baseline sequential, 1 = Extended sequential, 2 = Progressive) --> <!ATTLIST "sof" "samplePrecision" #CDATA "8"> <!-- The number of bits per sample --> <!-- Data type: Integer --> <!ATTLIST "sof" "numLines" #CDATA #IMPLIED> <!-- The number of lines in the image --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 65535 (inclusive) --> <!ATTLIST "sof" "samplesPerLine" #CDATA #IMPLIED> <!-- The number of samples per line --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 65535 (inclusive) --> <!ATTLIST "sof" "numFrameComponents" ("1" | "2" | "3" | "4") #IMPLIED> <!-- The number of components in the image --> <!ELEMENT "componentSpec" EMPTY> <!-- A component specification for a frame --> <!ATTLIST "componentSpec" "componentId" #CDATA #REQUIRED> <!-- The id for this component --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "componentSpec" "HsamplingFactor" #CDATA #REQUIRED> <!-- The horizontal sampling factor for this component --> <!-- Data type: Integer --> <!-- Min value: 1 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "componentSpec" "VsamplingFactor" #CDATA #REQUIRED> <!-- The vertical sampling factor for this component --> <!-- Data type: Integer --> <!-- Min value: 1 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "componentSpec" "QtableSelector" ("0" | "1" | "2" | "3") #REQUIRED> <!-- The quantization table to use for this component --> <!ELEMENT "sos" (scanComponentSpec)*> <!-- A Start Of Scan marker segment --> <!-- Min children: 1 --> <!-- Max children: 4 --> <!ATTLIST "sos" "numScanComponents" ("1" | "2" | "3" | "4") #REQUIRED> <!-- The number of components in the scan --> <!ATTLIST "sos" "startSpectralSelection" #CDATA "0"> <!-- The first spectral band included in this scan --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 63 (inclusive) --> <!ATTLIST "sos" "endSpectralSelection" #CDATA "63"> <!-- The last spectral band included in this scan --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 63 (inclusive) --> <!ATTLIST "sos" "approxHigh" #CDATA "0"> <!-- The highest bit position included in this scan --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 15 (inclusive) --> <!ATTLIST "sos" "approxLow" #CDATA "0"> <!-- The lowest bit position included in this scan --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 15 (inclusive) --> <!ELEMENT "scanComponentSpec" EMPTY> <!-- A component specification for a scan --> <!ATTLIST "scanComponentSpec" "componentSelector" #CDATA #REQUIRED> <!-- The id of this component --> <!-- Data type: Integer --> <!-- Min value: 0 (inclusive) --> <!-- Max value: 255 (inclusive) --> <!ATTLIST "scanComponentSpec" "dcHuffTable" ("0" | "1" | "2" | "3") #REQUIRED> <!-- The huffman table to use for encoding DC coefficients --> <!ATTLIST "scanComponentSpec" "acHuffTable" ("0" | "1" | "2" | "3") #REQUIRED> <!-- The huffman table to use for encoding AC coefficients --> ]>
Stream Metadata DTD
<!DOCTYPE "javax_imageio_jpeg_stream_1.0" [ <!ELEMENT "javax_imageio_jpeg_stream_1.0" (dqt | dht | dri | com | unknown)*> <!-- All elements are as defined above for image metadata --> ]>


Let Help Provide You Healthcare In The Best Way Possible
Get access to instant medical aid from our pool of experienced and adqeuate health practional which is now fast and easy. Talk to a doctor today.
200+
95%
20
Experienced
Doctors
Clinic
Locations
Positive
Feedback
How It Works
You can get started on Century Health in simply 4 step. Easy right? All you need do
Meet Our Experienced Doctors
At Century Health, highly experienced doctors are on call to attend to all your health issues and we are certained you will be handled with high priority
Dr. Kapur Ariv
Surgeon
Testimonials
Track Your Health History Periodically
A more convenient and affordable way to track your health and book your appointment anytime, anyday, anywhere. Download the App now and say yes to wellness.


Id: 169
Name: Jacob
Status: Alive
Species: Human
Type:
Gender: Male
OriginName: Earth (C-137)
OriginUrl: https://rickandmortyapi.com/api/location/1
LocationName: Earth (C-137)
LocationUrl: https://rickandmortyapi.com/api/location/1
Image: https://rickandmortyapi.com/api/character/avatar/169.jpeg
Episode: ['https://rickandmortyapi.com/api/episode/3', 'https://rickandmortyapi.com/api/episode/15']
Url: https://rickandmortyapi.com/api/character/169
Created: 2017-12-29T17:20:52.037Z


iOS UI Components and Conventions
Objective
In this section, you will learn some of the user interface and user experience components that are specific to iOS.
Contents
We've already covered many of the iOS UI conventions in the iOS Platform Overview chapter. In this section, we'll dig just a bit deeper into some iOS specifics. These include iPad-only UI controls, app badges, and integration with the platform's Settings app.
iPad-only UI APIs
The iPad includes two UI components that are not available with the iPhone. These are the Popover and SplitWindow.
Popovers
You use popovers to present information temporarily, but in a way that does not take over the entire screen like a modal view does. The popover content is layered on top of your existing content in a special window. The popover remains visible until the user taps outside of the popover window or explicitly dismisses it. The Popover is created by the method Titanium.UI.iPad.createPopover().
Popovers provide a way for you to display additional information associated with a particular UI component. This is shown in the picture above, in which event repetition options are shown as a row in a table. Within the popover, you can display a view or other component.
var button = Ti.UI.createButton({
title:'Show popover',
width:250,
height:50,
top:30,
right:5
});
var popover = Ti.UI.iPad.createPopover({
width:300,
height:250,
title:'I\'m a Popover',
arrowDirection:Ti.UI.iPad.POPOVER_ARROW_DIRECTION_RIGHT
});
button.addEventListener('click', function(e) {
popover.show({
view:button,
animated:true
});
});
win.add(button);
In the preceding code, line 16 creates the association between the button and the popover. It's this line that controls the placement of the popover. It will be placed automatically by iOS according to this association and the arrow direction. In this example, the arrow points to the right, which means the popover will be displayed to the left of the button. If you used POPOVER_ARROW_DIRECTION_UP, the popover would be shown below the button.
SplitWindows
A SplitWindow is a window that manages the presentation of two side-by-side view controllers. You use this class to implement a master-detail interface, in which the left-side view presents a list of items and the right-side presents details of the selected item. The SplitView is for use exclusively on iPad devices. The SplitWindow is created by the method Titanium.UI.iPad.createSplitWindow().
You cannot control the width of either of the view controllers, and the user cannot resize those "panes." A SplitWindow is a top-level container, which means you don't add it to a Ti.UI.Window. The SplitWindow inherits from the Window object.
// Window shown in narrower, left "pane"
var masterWin = Ti.UI.createWindow({
backgroundColor:'#fff'
});
masterWin.add(Ti.UI.createLabel({
text:'Master View',
top:10,
width:'auto',
height:'auto',
font:{
textAlign:'center',
fontWeight:'bold'
}
}));
// Window shown in wider, right "pane"
var detailWin = Ti.UI.createWindow({
backgroundColor:'#dfdfdf'
});
detailWin.add(Ti.UI.createLabel({
text:'Detail View',
top:10,
width:'auto',
height:'auto',
font:{
textAlign:'center',
fontWeight:'bold'
}
}));
// the split window
var splitwin = Ti.UI.iPad.createSplitWindow({
detailView:detailWin,
masterView:masterWin,
orientationModes:[ Titanium.UI.LANDSCAPE_LEFT,Titanium.UI.LANDSCAPE_RIGHT ]
});
splitwin.open();
Badges
Tab and app badges are indicators of updates, new messages, and so forth. You can apply these badges, with a numeric value, to your app and to tabs within your app. Doing so is quite simple. For tabs, simply set the yourTab.badge property equal to some number to show the tab badge icon. For your app, set the Ti.UI.iPhone.appBadge property equal to a number to show the app badge icon. You might do so when your app initializes or shuts down. You could also use a background service to set your app's badge as needed.
To remove either a tab or app badge, set the corresponding property to null. Setting it to 0 would still display the badge, but with the number zero displayed.
// Sets the app's icon badge to 23
Ti.UI.iPhone.appBadge = 23;
var tabGroup = Titanium.UI.createTabGroup();
var win1 = Titanium.UI.createWindow({
title:'Window 1',
backgroundColor:'#fff'
});
// Set the badge for this tab to 10
var tab1 = Titanium.UI.createTab({
icon:'KS_nav_views.png',
title:'Tab 1',
window:win1,
badge:10
});
Settings integration
Apple's guidelines specify that app settings should be configurable through the native Settings app rather than through in-app controls. Apple's own apps generally follow this recommendation. For example, you configure Safari's options via Settings rather than within the app. Titanium provides a mechanism for configuration options via the Settings app via a native module.
Download a recent copy of the KitchenSink app and copy the modules folder to your project. In Studio, expand modules/iphone/Settings.bundle and double-click Root.plist to open the Xcode Property List editor. You'll see the app's name defined in Item 0. The three preferences defined by KitchenSink appear under Items 1, 2, and 3. You can modify these to suit your needs or add new properties items following the pattern set out in these items.
In your app, you can access the values of these app preferences by using Titanium's App Properties module with one minor tweak. The name of the app property you retrieve must match the value you set in the Identifier field in the Property List editor. You'll note that all the KitchenSink examples include the "_preference" suffix, which you should also include with your identifiers.
Ti.API.info(Ti.App.Properties.getString('name_preference')); // logs the name pref value from KitchenSink example
Hands-on Practice
Goal
In this activity, you will enable Settings-integration for a simple iOS app and use the preferences configured by the user within the app.
Resources
To complete this activity, you will need an Intel-based Mac with the iOS development tools (Xcode) installed.
Steps
-
Create a new Titanium Mobile project named AppSettings that targets the iPhone and iPad.
-
If you have not done so already, download a copy of the KitchenSink project from https://github.com/appcelerator/KitchenSink. Copy the modules folder to your project's directory.
-
In Studio, expand modules/iphone/Settings.bundle and double-click Root.plist
-
Edit the name of the app in the plist file to match your app's name. You can leave the other settings options as-is. Save your changes and close Xcode.
-
Change label1 to display the name the user enters in the Settings app for the Name preference. If no value is set, display the text "Set Name in Settings" in the label.
-
If the "Enabled" switch is set to Yes (on) , display an ImageView that contains the KS_nav_ui.png graphic. If it's set to No (off) , don't display the image. You can use the Ti.App.Properties.getBool() method for this purpose.
-
Build and test your app. After it's installed and run the first time, open the Settings app and configure your name and Enabled value.
-
Because the app remains running (iOS doesn't fully close an app when you return to the Springboard), you'll need to close and relaunch the simulator or use the Fast App Switcher to kill the app. If using the simulator launched by Studio, using the Fast App Switcher might crash the simulator.
-
Close the AppSettings app, if necessary
-
Double-click the Home button to display the Fast App Switcher tray.
-
Press and hold on the AppSetting icon in the tray until it begins to shake. Tap its red minus-sign button to kill the app.
-
Press Home to leave "edit mode" then press Home again to return to the Springboard.
-
You can now re-open the app to see the new settings in place.
-
References and Further Reading
Summary
In this chapter, you learned that iOS offers a handful of UI components that are not available on other platforms. You learned how to implement iPad-specific controls and how to create and save app preferences in the Settings app.


IlluminesceSense
Mission and description of our team members
@(message: String) @Main("Home") {
Mission and description of our team members
Description of the project and link
Description of project and link
Breakthrough Innovation Challenege and the Business Plan Competition


Chanslate is a messaging app which you can use to:
To make it possible, Chanslate uses the following third party API:
Chanslate was created by musically_ut.
It was built using Meteor and heavily draws inspiration from ChatWorks. The background was provided by The Pattern Library. The sound for the notifications was provided by the Sound Bible. For CSS, Semantic UI was used.
This project can be found on Github at musically-ut/chanslate.
In case you were wondering, Chanslate = Chat + Translate or Chanslate = Chance + Translate. Take your pick.


Loading...
Standard disclaimer: the dataset has been heavily summarized from the original. Additionally, it has been heavily interpreted and it not endorsed by the Cleveland Museum of Art.
In my opinion, the best thing to do when starting to look at a museum's collections is to get some basic statistics. I find this helpful because it gives a sense for the order of magnitude of the dataset.
Total Objects
Departments
Types
% Public Domain
Next, I broke down the collections by department and type, to get a sense of distribution within the museum.
Count by Department
The interesting find here is that Prints is about 1/3 of the entire museum's collections, and has far more items than any other department. Meanwhile, the Educational Art department has only 1 item and some traditionally underrepresented cultures such as Africa and Islam have a very small number of items (which is unfortunately not unique to this museum; much larger museums also have this discrepency). However, the floor space isn't accounted for here, so lets look at a different chart.
Count on Display by Department
When looking at this chart, is becomes visible that Medieval and Decorative art are most of what's on display (~40%). Additionally, despite their large numbers in the collection, Prints, Drawings, and Photographs have very little representation. This is additionally interesting because the website and mission statement have a strong focus on "art" but little/no mention of these collections (and despite calling the Prints collection internationally known "for its high quality"). It's possible that the physical materials don't cope well with being on display, but there's still an insane discrepency here.
Count by Type
Note the scale on the y-axis; apologies, but you couldn't see much if I didn't do it.
Types tell us about the kinds of items a museum has, as opposed to style. In line with the departments, we can see that there are a large numbber of Prints, Photographs, and Drawings. It's also interesting to see a lot of Bound Volumes, which are books of materials, as opposed to just one on its own. It's also worth noting that for the top few departments, most of them have the same type as their department name. This poses the question whether "Print" or similar is the most specific a department can get, especially when there are many types with <10 items. Also, there's a Miscellaneous type with over 500 items, and I find it somewhat disappointing that this is the case; a small sample was mostly decorated boxes and cups, which I would suspect there are terms for.
Number of Types over Count by Department
This chart allows us to see if there is a correlation between the number of items in a department and how descriptive the Type information is. There does not seem to be an overall correlation, but if you look at the specific points, departments representing a culture tend to be higher up than ones describing a Type. Additionally, departments about Painting and Sculpture tend to have less types.
% Public Domain over Object Count by Department
As someone heavily engaged in open source, I find interesting to see how much of a museum's collection is public domain. This chart looks for correlation between the number of items in a department and how much of it is public domain. Similar to types, there is not a noticable correlation. Of note, Contemporary Art and Photography have very little public domain; this makes sense due to the age and rights of works those departments are likely to have. The big departments are more mixed, but tent to be below average for being public domain. That said, they also have a lot more items, which makes them harder to process.
Growth (Accessions) per Year
This chart doesn't tell us much for a few reasons, but it's interesting to see. This chart shows how many new items the collections gained each year. The reason this chart doesn't say much is that there are a variety of factors that influence accession. First, the museum may refuse items it doesn't think align with its mission or it can't care for them. It also doesn't account for the budget of the museum for active purchasing and the chance that something is donated. Finally, it doesn't account for things like a box of photos, which might all need to be individually accessioned, vs. a giant statue. However, what this chart can tell us is how the combination of factors such as these came together in the records.  You can see slight downturns over the course of major wars the US was involved in, but more noticeable is economic recessions such as around the 1930s and late 1970s (I haven't dug too deeply into this though).
Some Wild Speculation
This section is completely out-there and has no substantial basis for its claims. However, these are a loose trend I noticed while working through the data: culture-oriented departments seem to be more active than type-focused ones. To this point, culture-oriented departments were more descriptive in type of material, more public domain, and most importantly, more on display. This seems backed by the website, map, and mission statement, which talk about bringing art to people but have little mention of the Photography department, for example. Additionally, type-oriented departments seem to be more focused on processing what they have rather than showing it off, which may be related to this lack of description. Again, this section is literally titled "wild speculation" and should not be taken as fact.


ADPilatus¶
| author: | Mark Rivers (University of Chicago), John Hammonds, Brian Tieman (Argonne National Laboratory) |
|---|
Contents
Introduction¶
This is an areaDetector driver for the Pilatus pixel array detectors from Dectris.
The interface to the detector is via a TCP/IP socket interface to the
camserver
server that Dectris provides. The camserver program must
be started before the areaDetector software is started, typically by
running the camonly script provided by Dectris.
The camserver program saves the data to disk as TIFF or CBF files. The areaDetector software reads these disk files in order to read the data, because camserver does not provide another mechanism to access the data. If camserver is saving TIFF files then the driver reads the TIFFImageDescription tag from the TIFF file. This is a long string that camserver writes to the file containing all of the detector settings, including threshold, energy, etc. The driver adds this information to the NDArray using an NDAttribute called TIFFImageDescription. The NDFileTIFF plugin in ADCore R2-6 was changed to write this complete attribute to the TIFFImageDescription tag in the new TIFF file. It will also be written by the NDFileNetCDF, NDFileHDF5, and NDFileNexus plugins. However these plugins are currently limited to 256 character string attributes, so some of the information will be lost because the string is longer than 256 characters.
This driver inherits from ADDriver. It implements nearly all of the parameters in asynNDArrayDriver.h and in ADArrayDriver.h. It also implements a number of parameters that are specific to the Pilatus detectors. The pilatusDetector class documentation describes this class in detail.
Implementation of standard driver parameters¶
The following table describes how the Pilatus driver implements some of the standard driver parameters.
| Parameter Definitions in pilatusDetector.cpp and EPICS Record Definitions in pilatus.template | ||
|---|---|---|
| Parameter index variable | EPICS record name | Description |
| ADTriggerMode | $(P)$(R)TriggerMode | The driver redefines the choices for the ADTriggerMode parameter (record $(P)$(R)TriggerMode) from ADDriver.h. The choices for the Pilatus are: Internal (external signal not used) External Enable (count while external trigger line is high, readout on high to low transition) External Trigger (begin acquisition sequence on high to low transition of external trigger line) Multiple External Trigger (high to low transition on external signal triggers a single acquisition for the programmed exposure time) Alignment (collect images as fast as exposure time and readout permit, images written to a temporary file) The first 4 modes correspond directly to the camserver commands Exposure, ExtEnable, ExtTrigger, and ExtMTrigger respectively. Alignment mode uses the Exposure command as well, but continuously takes images into the same temporary file (alignment.tif). |
| ADAcquireTime | $(P)$(R)AcquireTime | Controls the acquisition time in all modes except External Enable. In External Enable mode the timing is controlled entirely by the external trigger line. However, even in ExternalEnable mode AcquireTime is used by camserver and by the driver to estimate how long the acquisition will take. Hardware timeouts will occur if the actual time to acquire differs significantly from the estimated time based on AcquireTime and AcquirePeriod. |
| ADNumImages | $(P)$(R)NumImages | Controls the number of images to acquire. It applies in all trigger modes except Alignment. |
| ADAcquirePeriod | $(P)$(R)AcquirePeriod | Controls the exposure period in seconds in Internal or External Trigger modes when NumImages >1. In External Enable mode the timing is controlled entirely by the external trigger line. However, even in ExternalEnable mode AcquirePeriod is used by camserver and by the driver to estimate how long the acquisition will take. Hardware timeouts will occur if the actual time to acquire differs significantly from the estimated time based on AcquireTime and AcquirePeriod. |
| ADNumExposures | $(P)$(R)NumExposures | Controls the number of exposures per image. It is most useful in External Enable mode, but it can be set in any mode. |
| ADAcquire | $(P)$(R)Acquire | Controls the acquisition. Setting this to 1 starts image acquisition. The driver sets the record to 0 when acquisition is complete. This means an entire acquisition series if NImages >1. Setting this to 0 aborts an acquisition. If the driver was currently acquiring imges then this record will cause the “Stop” and “K” (Kill) commands to be sent to camserver. |
| NDFilePath | $(P)$(R)FilePath | Controls the path for saving images. It must be a valid path for camserver and for the areaDetector driver, which is normally running in an EPICS IOC. If camserver and the EPICS IOC are not running on the same machine then soft links will typically be used to make the paths look identical. |
| NDFileTemplate | $(P)$(R)FileTemplate | camserver uses the file extension to determine what format to save the files in. The areaDetector Pilatus driver only supports TIFF and CBF files, so the extension should be .tif or .cbf. When saving multiple images (NImages>1) camserver has its own rules for creating the names of the individual files. The rules are as follows. The name constructed using the algorithm described for NDFileTemplate under File Saving Parameters is used as a basename. The following examples show the interpretation of the basename.
|
It is useful to load and enable an NDPluginStats plugin that gets its data from the Pilatus driver. The MaxValue_RBV PV for that plugin can be monitored to make sure that the 20-bit limit of 1,048,575 is not being approached in any pixel.
Pilatus specific parameters¶
The Pilatus driver implements the following parameters in addition to
those in asynNDArrayDriver.h and ADDriver.h:. Note that to reduce the
width of this table the parameter index variable names have been split
into 2 lines, but these are just a single name, for example
PilatusDelayTime
.
| Parameter Definitions in pilatusDetector.cpp and EPICS Record Definitions in pilatus.template* | |||
|---|---|---|---|
| Description | drvInfo string | EPICS record name | EPICS record type |
| Delay in seconds between the external trigger and the start of image acquisition. It only applies in External Trigger mode | DELAY_TIME | $(P)$(R)DelayTime | ao |
| Threshold energy in keV. camserver uses this value to set the discriminators in each pixel. It is typically set to the incident x-ray energy ($(P)$(R)Energy), but sometimes other values may be preferable. | THRESHOLD | $(P)$(R)ThresholdEnergy, $(P)$(R)ThresholdEnergy_RBV | ao, ai |
| Apply the threshold value. Setting the threshold can be a time consuming operation, so if ThresholdAutoApply is No then ThresholdApply must be processed to actually send the threshold to camserver. | THRESHOLD_APPLY | $(P)$(R)ThresholdApply | busy |
| Apply the threshold value. Setting the threshold can be a time consuming operation, so if ThresholdAutoApply is No then ThresholdApply must be processed to actually send the threshold to camserver. | THRESHOLD_APPLY | $(P)$(R)ThresholdApply | busy |
| Automatically apply the threshold value whenever it changes. Setting the threshold can be a time consuming operation, so if ThresholdAutoApply is No then ThresholdApply must be processed to actually send the threshold to camserver. If it is Yes then the threshold value will be sent to camserver whenever it is changed. | THRESHOLD_AUTO_APPLY | $(P)$(R)ThresholdAutoApply, $(P)$(R)ThresholdAutoApply_RBV | bo, bi |
| X-ray energy in keV. This is used by camserver to calculate the proper flat field corrrection. If Energy is 0 then the energy value sent to camserver is ThresholdEnergy*2. | ENERGY | $(P)$(R)Energy, $(P)$(R)Energy_RBV | ao, ai |
| The value that camserver should write to the data file for the gaps between pixels in the detector. Choices are -2, 0, and -1. | GAP_FILL | $(P)$(R)GapFill, $(P)$(R)GapFill_RBV | mbbo, mbbi |
This is only used on older Pilatus detectors, not the newer ones. |
N/A | $(P)$(R)GainMenu | mbbo |
| Flag to indicate when the Pilatus is ready to accept external trigger signals (0=not ready, 1=ready). This should be used by clients to indicate when it is OK to start sending trigger pulses to the Pilatus. If pulses are send before Armed=1 then the Pilatus may miss them, leading to DMA timeout errors from camserver | ARMED | $(P)$(R)Armed | bi |
| Processing this record sends the “ResetModulePower delayTime” command to camserver. This cycles the high voltage and other detector supply voltages. This is particularly useful with the CdTe detectors when they have been exposed to a very strong signal. Cycling the power can erase the memory effects of oversaturation. The delayTime controls the amount of time to wait after turning off the power before turning it back on again. It is set by the ResetPowerTime record. | RESET_POWER | $(P)$(R)ResetPower | bo |
| This record controls the number of seconds to wait after turning off the power before turning it back on again when the ResetPower record is processed. | RESET_POWER_TIME | $(P)$(R)ResetPower, $(P)$(R)ResetPower_RBV | bo, bi |
| Timeout in seconds when reading a TIFF or CBF file. It should be set to several seconds, because there can be delays for various reasons. One reason is that there is sometimes a delay between when an External Enable acquisition is started and when the first external pulse occurs. Another is that it can take some time for camserver processes to finish writing the files. | IMAGE_FILE_TMOT | $(P)$(R)ImageFileTmot | ao |
Name of a file to be used to replace bad pixels. If this record does not point to a valid bad pixel file then no bad pixel mapping is performed. The bad pixel map is used before making the NDArray callbacks. It does not modify the data in the files that camserver writes. This is a simple ASCII file with the following format: badX1,badY1 replacementX1,replacementY1
badX2,badY2 replacementX2,replacementY2
...
The X and Y coordinates range from 0 to NXPixels-1 and NYPixels-1. Up to 100 bad pixels can be defined. The bad pixel mapping simply replaces the bad pixels with another pixel’s value. It does not do any averaging. It is felt that this is sufficient for the purpose for which this driver was written, namely fast on-line viewing of ROIs and image data. More sophisticated algorithms can be used for offline analysis of the image files themselves. The following is an example bad pixel file for a GSECARS detector: 263,3 262,3
264,3 266,3
263,3 266,3
300,85 299,85
300,86 299,86
471,129 472,129
|
BAD_PIXEL_FILE | $(P)$(R)BadPixelFile | waveform |
| The number of bad pixels defined in the bad pixel file. Useful for seeing if the bad pixel file was read correctly. | NUM_BAD_PIXELS | $(P)$(R)NumBadPixels | longin |
Name of a file to be used to correct for the flat field. If this record does not point to a valid flat field file then no flat field correction is performed. The flat field file is simply a TIFF or CBF file collected by the Pilatus that is used to correct for spatial non-uniformity in the response of the detector. It should be collected with a spatially uniform intensity on the detector at roughly the same energy as the measurements being corrected. When the flat field file is read, the average pixel value (averageFlatField) is computed using all pixels with intensities > PilatusMinFlatField. All pixels with intensity <PilatusMinFlatField in the flat field are replaced with averageFlatField. When images are collected before the NDArray callbacks are performed the following per-pixel correction is applied: ImageData[i] =
(averageFlatField *
ImageData[i])/flatField[i];
|
FLAT_FIELD_FILE | $(P)$(R)FlatFieldFile | waveform |
| The mimimum valid intensity in the flat field. This value must be set > 0 to prevent divide by 0 errors. If the flat field was collected with some pixels having very low intensity then this value can be used to replace those pixels with the average response. | MIN_FLAT_FIELD | $(P)$(R)MinFlatField | longout |
| This record indicates if a valid flat field file has been read. 0=No, 1=Yes. | FLAT_FIELD_VALID | $(P)$(R)FlatFieldValid | bi |
| MX wavelength to write to CBF and TIFF image header. | WAVELENGTH | $(P)$(R)Wavelength | ao |
| MX energy range low value to write to CBF and TIFF image header. | ENERGY_LOW | $(P)$(R)EnergyLow | ao |
| MX energy range high value to write to CBF and TIFF image header. | ENERGY_HIGH | $(P)$(R)EnergyHigh | ao |
| MX detector distance to write to CBF and TIFF image header. | DET_DIST | $(P)$(R)DetDist | ao |
| MX detector vertical offset to write to CBF and TIFF image header. | DET_VOFFSET | $(P)$(R)DetVOffset | ao |
| MX beam X to write to CBF and TIFF image header. | BEAM_X | $(P)$(R)BeamX | ao |
| MX beam Y to write to CBF and TIFF image header. | BEAM_Y | $(P)$(R)BeamY | ao |
| MX flux to write to CBF and TIFF image header. | FLUX | $(P)$(R)Flux | ao |
| MX filter transmission to write to CBF and TIFF image header. | FILTER_TRANSM | $(P)$(R)FilterTransm | ao |
| MX start angle to write to CBF and TIFF image header. When saving multiple images (ADNumImages>1) camserver will automatically increment the field in the image header by PilatusAngleIncr for each image. | START_ANGLE | $(P)$(R)StartAngle | ao |
| MX angle increment to write to CBF and TIFF image header. When saving multiple images (ADNumImages>1) camserver will automatically increment the field corresponding to PilatusStartAngle in the image header by this value for each image. | ANGLE_INCR | $(P)$(R)AngleIncr | ao |
| MX detector 2theta to write to CBF and TIFF image header. | DET_2THETA | $(P)$(R)Det2theta | ao |
| MX polarization to write to CBF and TIFF image header. | POLARIZATION | $(P)$(R)Polarization | ao |
| MX alpha to write to CBF and TIFF image header. | ALPHA | $(P)$(R)Alpha | ao |
| MX kappa to write to CBF and TIFF image header. | KAPPA | $(P)$(R)Kappa | ao |
| MX phi to write to CBF and TIFF image header. | PHI | $(P)$(R)Phi | ao |
| MX phi increment to write to CBF and TIFF image header | PHI_INCR | $(P)$(R)PhiIncr | ao |
| MX chi to write to CBF and TIFF image header. | CHI | $(P)$(R)Chi | ao |
| MX chi increment to write to CBF and TIFF image header | CHI_INCR | $(P)$(R)ChiIncr | ao |
| MX omega to write to CBF and TIFF image header. | OMEGA | $(P)$(R)Omega | ao |
| MX omega increment to write to CBF and TIFF image header | OMEGA_INCR | $(P)$(R)OmegaIncr | ao |
| MX oscillation axis text, up to 18 characters in length, to write to CBF and TIFF image header. | OSCILL_AXIS | $(P)$(R)OscillAxis | stringout |
| MX number of oscillations to write to CBF and TIFF image header. | NUM_OSCILL | $(P)$(R)NumOscill | longout |
| Template file to be used to obtain all of the MX parameters above. Set the name of this file to “0” to disable the template file. | CBFTEMPLATEFILE | $(P)$(R)CbfTemplateFile | waveform |
| Header string to write in the COMMENT field of the CBF file. | HEADERSTRING | $(P)$(R)HeaderString | waveform |
| Maximum possible count rate per pixel. | PIXEL_CUTOFF | $(P)$(R)PixelCutOff_RBV | ai |
| Temperature readout 0. | TH_TEMP_0 | $(P)$(R)Temp0_RBV | ai |
| Temperature readout 1. | TH_TEMP_1 | $(P)$(R)Temp1_RBV | ai |
| Temperature readout 2. | TH_TEMP_2 | $(P)$(R)Temp2_RBV | ai |
| Humidity readout 0. | TH_HUMID_0 | $(P)$(R)Humid0_RBV | ai |
| Humidity readout 1. | TH_HUMID_1 | $(P)$(R)Humid1_RBV | ai |
| Humidity readout 2. | TH_HUMID_2 | $(P)$(R)Humid2_RBV | ai |
| Version of TVX and camserver. This record is redundant with the SDKVersion_RBV record added in ADCore R2-6, which should be used instead. | TVXVERSION | $(P)$(R)TVXVersion_RBV | stringin |
asyn record to control debugging communication with camserver.
Setting the CNCT field in this record to Disconnect causes the drvAsynIPPort server to disconnect from camserver.
This can be used to allow another program, such as TVX, to temporarily take control of camserver, without restarting the EPICS IOC.
Set CNCT to Connect to reconnect the IOC to camserver, or simply process any record which communicates with camserver,
because the driver will automatically reconnect. |
N/A | $(P)$(R)CamserverAsyn | asyn |
Configuration¶
The pilatusDetector driver is created with the pilatusDetectorConfig command, either from C/C++ or from the EPICS IOC shell.
int pilatusDetectorConfig(const char *portName, const char *camserverPort,
int maxSizeX, int maxSizeY,
int maxBuffers, size_t maxMemory,
int priority, int stackSize)
For details on the meaning of the parameters to this function refer to the detailed documentation on the pilatusDetectorConfig function in the pilatusDetector.cpp documentation and in the documentation for the constructor for the pilatusDetector class.
Example st.cmd startup file¶
The following startup script is provided with ADPilatus.
< envPaths
errlogInit(20000)
dbLoadDatabase("$(TOP)/dbd/pilatusDetectorApp.dbd")
pilatusDetectorApp_registerRecordDeviceDriver(pdbbase)
# Prefix for all records
epicsEnvSet("PREFIX", "13PIL1:")
# The port name for the detector
epicsEnvSet("PORT", "PIL")
# The queue size for all plugins
epicsEnvSet("QSIZE", "20")
# The maximim image width; used for row profiles in the NDPluginStats plugin
epicsEnvSet("XSIZE", "487")
# The maximim image height; used for column profiles in the NDPluginStats plugin
epicsEnvSet("YSIZE", "195")
# The maximum number of time seried points in the NDPluginStats plugin
epicsEnvSet("NCHANS", "2048")
# The maximum number of frames buffered in the NDPluginCircularBuff plugin
epicsEnvSet("CBUFFS", "500")
# The search path for database files
epicsEnvSet("EPICS_DB_INCLUDE_PATH", "$(ADCORE)/db")
###
# Create the asyn port to talk to the Pilatus on port 41234.
drvAsynIPPortConfigure("camserver","gse-pilatus1:41234")
# Uncomment the following to enable asynTrace on the camserver port
#asynSetTraceIOMask("camserver",0,2)
#asynSetTraceMask("camserver",0,9)
# Set the input and output terminators.
asynOctetSetInputEos("camserver", 0, "\0x18")
asynOctetSetOutputEos("camserver", 0, "\n")
pilatusDetectorConfig("$(PORT)", "camserver", $(XSIZE), $(YSIZE), 0, 0)
dbLoadRecords("$(ADPILATUS)/db/pilatus.template","P=$(PREFIX),R=cam1:,PORT=$(PORT),ADDR=0,TIMEOUT=1,CAMSERVER_PORT=camserver")
# Create a standard arrays plugin
NDStdArraysConfigure("Image1", 5, 0, "$(PORT)", 0, 0)
dbLoadRecords("$(ADCORE)/db/NDStdArrays.template", "P=$(PREFIX),R=image1:,PORT=Image1,ADDR=0,TIMEOUT=1,NDARRAY_PORT=$(PORT),TYPE=Int32,FTVL=LONG,NELEMENTS=94965")
# Load all other plugins using commonPlugins.cmd
< $(ADCORE)/iocBoot/commonPlugins.cmd
set_requestfile_path("$(ADPILATUS)/pilatusApp/Db")
# Uncomment to enable asynTrace on the driver port
#asynSetTraceMask("$(PORT)",0,255)
iocInit()
# save things every thirty seconds
create_monitor_set("auto_settings.req", 30,"P=$(PREFIX)")
MEDM screens¶
The following show the MEDM screens that are used to control the Pilatus detector. Note that the general purpose screen ADBase.adl can be used, but it exposes many controls that are not applicable to the Pilatus.
pilatusDetector.adl
is the main screen used to control the Pilatus
driver.
pilatusAncillary.adl
is the screen used to control define the
metadata that will be written to the Pilatus data file.
NDROI4.adl
is used to define the ROIs. In this example there are 3
valid ROIs defined. ROI 1 is the entire detector, ROI 2 is a 300x50
rectangle starting at [100,60], and ROI 3 is a 50x30 rectangle starting
at [220,70].
NDStats5.adl
is used to display the statistics in the ROIs defined
above.
mca.adl or mca_small.adl
can be used to plot the net or total counts
in an ROI when NImages>1. In this example the plot is the net counts in
ROI 1 as the diffractometer chi was scanned +- 1 degree with 1000 points
at .02 seconds/point. This was done with the SPEC command:
lup chi -1 1 1000 .02
using trajectory scanning on a Newport kappa diffractometer. This was a compound motor scan with the Newport XPS putting out pulses every .02 seconds. These pulses triggered the Pilatus in External Enable mode. The Pilatus driver read each TIFF file as it was created and updated this plot every 0.2 seconds. The total time to collect this scan with 1000 images was 20.8 seconds.
scan_more.adl
is used to define a scan. In this example the sscan
record is set up to scan the ThresholdEnergy PV and to collect the total
counts in ROI2, which was defined to include the entire detector.
scanDetPlot.adl
is used to plot the results of a scan after it is
complete. In this example the total counts in ROI 1 are plotted as a
function of the ThresholdEnergy as it was scanned from 3000 to 10000 eV
in 250 eV steps. The source was Fe55, and the cut-off is at 6 keV, as
expected for the Mn Ka and Mn Kb x-rays that this source produces.
asynRecord.adl
is used to control the debugging information printed
by the asyn TCP/IP driver for camserver (asynTraceIODriver).
asynOctet.adl
can be used to send any command to camserver and
display the response. It can be loaded from the More menu in
asynRecord.adl above.
SPEC interface¶
At the GSECARS beamlines (13-ID-C and 13-BM-C) at the APS we use SPEC to control our Newport diffractometers. We have added and modified SPEC macros to use the pilatusDetector areaDetector driver to treat the Pilatus detector as a SPEC counter. This works in both traditional step-scanning mode, as well as in trajectory scanning mode. Here are some snippets from the SPEC macros for the Pilatus.
# PILATUS_PREFIX detector name i.e. (GSE-PILATUS1:)
if ( PILATUS_PREFIX == "") PILATUS_PREFIX = "GSE-PILATUS1:"
PILATUS_PREFIX = getsval("Enter PILATUS detector name i.e. GSE-PILATUS1:",PILATUS_PREFIX)
# PILATUS_DET_PREFIX is the pv used by areaDetector to identify a specific detector.
# When only one detector is used it is usally (cam1:)
if ( PILATUS_DET_PREFIX == "") PILATUS_DET_PREFIX = "cam1:"
PILATUS_DET_PREFIX = getsval("Enter PILATUS specific detector name i.e. cam1:",PILATUS_DET_PREFIX)
# PILATUS_ROI_PREFIX is the pv used by areaDetector to identify a specific a ROI plugin.
# When only one ROI plugin is used it is usally (ROI1:)
if ( PILATUS_ROI_PREFIX == "") PILATUS_DET_PREFIX = "ROI1:"
PILATUS_ROI_PREFIX = getsval("Enter PILATUS ROI plugin name i.e. ROI1:",PILATUS_ROI_PREFIX)
if (PILATUS_MOUNT == "") PILATUS_MOUNT = "cars5/Data"
PILATUS_MOUNT = getsval("Enter mount point relative to camserver home directory",PILATUS_MOUNT)
if (PILATUS_SPEC_MOUNT == "") PILATUS_SPEC_MOUNT = "cars5/Data"
PILATUS_SPEC_MOUNT = getsval("Enter mount point relative to spec home directory",PILATUS_SPEC_MOUNT)
...
PILATUS_ROI_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:Net_RBV"
PILATUS_ROI_ARRAY_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:NetArray"
PILATUS_ROI_ARRAY_START_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:NetArrayEraseStart"
PILATUS_ROI_ARRAY_NUSE_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:NetArray.NUSE"
PILATUS_ROI_ARRAY_ACQ_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:NetArray.ACQG"
PILATUS_IMGPATH_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "FilePath"
PILATUS_FNAME_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "FileName"
PILATUS_FILENUMBER_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "FileNumber"
PILATUS_FILEFORMAT_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "FileTemplate"
PILATUS_EXPSRTM_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "AcquireTime"
PILATUS_NFRAME_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "NumImages"
PILATUS_EXPPRD_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "AcquirePeriod"
PILATUS_NEXPFRM_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "NumExposures"
PILATUS_ACQ_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "Acquire"
PILATUS_ARMED_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "Armed"
PILATUS_ABORT_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "Acquire"
PILATUS_ACQMODE_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "TriggerMode"
PILATUS_THRESHOLD_PV = PILATUS_PREFIX PILATUS_DET_PREFIX "ThresholdEnergy"
PILATUS_ROI_0_MinX_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:MinX"
PILATUS_ROI_0_SizeX_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:SizeX"
PILATUS_ROI_0_MinY_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:MinY"
PILATUS_ROI_0_SizeY_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:SizeY"
PILATUS_ROI_0_BgdWidth_PV = PILATUS_PREFIX PILATUS_ROI_PREFIX "0:BgdWidth"
...
def epics_pilatus_count '{
...
# Call macro that creates and set the Pilatus path and filename
img_full_filename
# Setup exposure time, collection mode and number of frames
epics_put(PILATUS_FILENUMBER_PV,NPTS, 1)
epics_put(PILATUS_NFRAME_PV, 1, 1)
epics_put(PILATUS_ACQMODE_PV,0, 1) # Internal trigger
epics_put(PILATUS_EXPSRTM_PV,cnt_time_val, 1)
epics_put(PILATUS_NEXPFRM_PV, 1, 1)
...
# hit the triggers
epics_put(PILATUS_ACQ_PV,1)
epics_put(sc_cnt_pv,1)
# wait for scaler and Pilatus AQG to finish
status = 1
sc_done = FALSE
img_done = FALSE
data_done = FALSE
while(status){
# is the scalar done
if (epics_get(sc_cnt_pv)=="Done"){
sc_done = TRUE;
#p "scaler done"
}
# is the pilatus done
if (epics_get(PILATUS_ACQ_PV) == "Done"){
img_done = TRUE;
#p "image collection done"
}
if( (sc_done==TRUE) && (img_done==TRUE)) break;
sleep(0.01)
}
# use the get_counts routine to read the scalers
# note get_counts also calls user_getcounts
# thats where the rois get read.
get_counts
}'
def user_getcounts '{
...
# using image_count routine
} else if ( EPICS_COUNT == 4 ) {
S[iroi] = 0
S[iroi] = epics_get(PILATUS_ROI_PV)
Performance measurements¶
The following measurements were done to demonstrate the performance that can be obtained with the areaDetector Pilatus driver.
AcquireMode=Internal, NumImages=1000, AcquireTime=.005, AcquirePeriod=.01, NumExposures=1. The time to collect this series should be exactly 10.0 seconds. The actual time was measured using the EPICS camonitor program. It printed the time when acquisition was started (Acquire changed to Acquire=1) and when acquisition was complete (Acquire changed to Done=0). The time was 10.022 seconds. This includes the time for camserver to save all 1000 images to disk (366 MB), and for the driver to read each file, correct the bad pixels and flat field, compute the ROIs, and post the ROIs to EPICS. It also posted all of the images to EPICS. The total additional time was less than 0.03 seconds for all 1000 images.
AcquireMode=Internal, NImages=1, ExposureTime=.01, NExposures=1. An EPICS sscan record was used to collect 1000 points. There were no positioner PVs (to eliminate motor overhead). The only detector trigger was the Pilatus Acquire PV. The only detector PV was ROI1:0:Total_RBV. In this mode camserver is being told to individually collect each file. If there were no overhead then time to collect this series should be exactly 10.0 seconds. The actual time measured using the EPICS camonitor program was 45.514 seconds. The overhead is thus 35.514 seconds, or 35 ms per point. In this single-frame mode the driver is thus able to collect >20 images/second. For comparison, another measurement was done using the same EPICS sscan record, but using a Joerger VSC16 scaler as the detector trigger and detector. The preset time was also .01 seconds. The elapsed time for a 1000 point scan was 16.068 seconds, so the overhead was 6.068 seconds, or 6 ms per point.
AcquireMode=Ext. Enable, NImages=1000, NExposures=1. SPEC was used to collect 1000 points using trajectory scanning mode with the Newport XPS motor controller. The following SPEC command was used::
lup chi -1 1 1000 .02
This tells SPEC to do a relative scan of the chi axis from -2 degrees to +2 degrees with 1000 points at .015 seconds/point. On our kappa diffractometer this entails a coordinated motion of the phi, kappa and omega axes. The EPICS trajectory scanning software downloads the non-linear trajectory that SPEC computes into the XPS controller, which executes it. As the motors are moving the XPS outputs synchronization pulses at the period of the collection time, .020 seconds in this case. These pulses are used as the external trigger to the Pilatus. The time to execute this scan should be 20.0 seconds. The actual time was 20.8 seconds, measured using camonitor on the Acquire PV. Again, this includes the time for camserver to save all 1000 images to disk (366 MB), and for the Pilatus driver to read each file, correct the bad pixels and flat field, compute the ROIs, and post the ROIs to EPICS. It also posted all of the images to EPICS. The total additional time was less than 0.8 seconds for all 1000 images. As soon as the acquisition was complete SPEC plotted the net counts in the first ROI (containing the Bragg peak) as follows:
For comparison this identical scan was executed in traditional step-scanning mode, where the motors stopped at each point in the scan. The Pilatus was run in Internal mode with NumImages=1. The total time for the scan was 870 seconds (more than 14 minutes), compared to 20.8 seconds in trajectory mode. Most of this overhead is the settling time for the motors, with only a small fraction due to the Pilatus single-exposure mode. The trajectory scanning mode is thus more than 50 times faster to execute the identical SPEC scan.
Hardware notes¶
Trigger pulses¶
The Pilatus supports 3 types of external triggering. In External Trigger mode (the camserver ExtTrigger command) the Pilatus uses the programmed values of AcquireTime, AcquirePeriod, NImages and NExposures. It waits for a single external trigger, then waits for Delay seconds and then collects the entire sequence. It is very similar to Internal mode with NImages>1, except that it waits for a trigger to begin collecting the sequence.
In External Enable mode (the camserver ExtEnable command) the Pilatus uses the external signal to control acquisition. Only NImages and NExposures are used, AcquireTime and AcquirePeriod are not used. When the signal is high the detector counts, and on the transition to low it begins its readout.
In External MultiTrigger Mode (the camserver ExtMTrigger command) the Pilatus uses the programmed AcquireTime, in addition to NImages and NExposures. Each external trigger pulse causes the Pilatus to collect one image at the programmed exposure time. This mode works well with a trigger source like the Newport motor controllers or the SIS380x multichannel scaler, that put out a short trigger pulse for each image. One only needs to take care that the time between external trigger pulses is at least 4msec longer than the programmed exposure time, to allow time for the detector to read out before the next trigger pulse arrives.
When using the External Enable mode, we use an inexpensive analog pulse generator to convert the trigger pulses from the MM4005 and XPS to a form suitable for External Enable mode with the Pilatus. This is the solution we have developed that seems to be reliable:
- The synchonization pulses from the Newport MM4005 or XPS controller are input into the external next pulse (channel advance, control signal 1) input of the SIS3801 multiscaler. This is the normal configuration used for MCS counting without the Pilatus in trajectory scanning mode.
- The Copy In Progress (CIP) output of the SIS3801 (control signal 5)
is connected to the Trigger Input of a Tenma TGP110 10 MHz Pulse
Generator. CIP will output a pulse whenever the SIS3801 does a
channel advance, either in external mode with the motor controller
pulse input, or in internal timed channel advance mode. The TGP100
Pulse Generator is configured as follows:
- Trigger Input connected to CIP output of SIS3801.
- Triggered mode.
- Complement output.
- Pulse duration set with knobs to 3msec.
- TTL Output connected to the External Input of the Pilatus.
- With this configuration the SIS3801 CIP output is normally at 5V, and outputs a 0V pulse 1 microsecond long. The trailing (rising) edge of that pulse triggers the TGP110. The TGP110 TTL output is also normally at 5V, and outputs a 0V pulse 3 milliseconds long each time the SIS3801 pulses. That output is connected to the Pilatus External Input. In External Enable mode when Pilatus External Input is high the Pilatus is counting. When the External Input is low the Pilatus reads out. The readout time is set via the knobs on the pulse generator to be 3 ms, which is close to the minimum time allowed on the Pilatus.
The Tenma TGP110 seems to be currently called a Tenma 72-6860, and lists for about $350 new at Newark.
Detector Voltage¶
When we were initially testing the Pilatus in the lab, we had many errors in External Enable mode, where it did not seem to be seeing the external pulses. camserver would get DMA timeouts, and need to be restarted. Dectris said these were happening because the cables on our detector are longer than normal, and the voltage drop from the power supply to the detector was leading to marginal voltage values. They suggested shortening the cables or increasing the supply voltage slightly. When moving the detector to the hutch these problems initially went away. However, they then recurred, and we fixed the problem by increasing the power supply voltage from 4.4 to 4.7 volts at the detector.
Dectris has since informed me that they have increased the power supply voltage on all new Pilatus systems, so this should no longer be an issue.
Restrictions¶
The following are some current restrictions of the areaDetector Pilatus driver:
- Limited to TIFF or CBF file format. camserver can save files in other formats, but the driver can currently only read TIFF and CBF files. It uses the standard libtiff library to read the TIFF files, so it should work on big or little endian machines, and should work with uncompressed or compressed files. It has only been tested with uncompressed files on a little-endian machine. It uses the CBFlib library to read the CBF files.
- The EPICS IOC should be run on the same computer as camserver. This is not strictly necessary, and places a small additional load on the CPU and network on that computer. However, we have found that TIFF files are available to be read within 10ms after camserver says they have been written if the IOC is running on the same machine as camserver. This is true even if the files are being saved on a remote NFS or SMB file system. On the other hand, if the IOC and camserver are running on separate machines, then the filesystem can wait up to 1 second after camserver says the TIFF file has been written before the IOC can read it. This is true even if the files are being written to the computer that the IOC is running on! This 1 second delay is often unacceptable for fast single-exposure scans, i.e. with NImages=1.
- The Pilatus driver keeps retrying to read each image file until the modification date of the image file is after the time that the acquisition was started. If it did not do this check then it could be reading and displaying old files that happen to have the same name as the current files being collected. This check requires that the computer that is running the soft IOC must have its clock well synchronized with the clock on the computer on which the files are being written (i.e. the computer generating the file modification time). If the clocks are not synchronized then the files may appear to be stale when they are not, and the driver will time out. The driver actually tolerates up to 10 second clock skew betweeen the computers but any more than this may lead to problems.
- Setting Acquire to 0 does not always stop acquisition immediately because camserver does not reliably implement the “K” command to stop an exposure sequence. In particular with NumImages>1 camserver seems to often ignore the K command completely, even with exposure times/periods as long as 10 seconds. With NumImages=1 it does kill the exposure after a few seconds.
- The following items are hardcoded in the driver. They can be changed
by compiling if necessary.
- MAX_MESSAGE_SIZE=256 The maximum size of message to/from camserver.
- MAX_FILENAME_LEN=256 The maximum size of a complete file name including path and extension.
- FILE_READ_DELAY=.01 seconds. The time between polling to see if the image file exists or if it is the expected size.
- MAX_BAD_PIXELS=100 The maximum number of bad pixels.


Steven Gerrard: Liverpool legend backs 'faultless' Van Dijk for Ballon d'Or
- Steven Gerrard believes Virgil van Dijk deserves to win 2019 Ballon d'Or award
- The Liverpool legend says he is convinced the Dutchman star had a remarkable 2019 above others
- Van Dijk will face stiff competition from Juventus star Cristiano Ronaldo and Argentine forward Lionel Messi
Steven Gerrard has tipped Liverpool center back, Virgil van Dijk, for the Ballon d'Or 2019 award glory ahead of Lionel Messi and Cristiano Ronaldo.
The Dutch star faces stiff competition from the Juventus forward and Barcelona star for this year's edition of the award slated for December.
Van Dijk failed to win Premier League but enjoyed a prosperous season under Jurgen Klopp as he was an integral part of the indomitable Liverpool squad that won Champions League.
And Gerrard, who captained Liverpool to their historical Champions League win over AC Milan in 2005, believes the centre-back deserves to win the award "100 percent" for his "faultless" performance.
Super Eagles star reaches big milestone, hailed as best midfielder in Premier League
The Rangers manager praises Lionel Messi for guiding Barcelona to domestic glory but submitted no players deserves the individual gong than the Dutch star, complementing a position earlier made by Klopp on the individual glory.
"I'm Messi's number one fan, for sure. I love the player. Obscene numbers in his game in terms of assists and goals. I'm definitely a Messi fan, in the gang," Gerrard said.
"But if you talk about consistency from one player over a year, who wins the European Cup and is absolutely faultless in every performance, that means you deserve to win the Ballon d'Or."
Van Dijk had earlier beaten Messi and Ronaldo to UEFA best men player award in August but trailed behind the Barcelona star in FIFA The Best award in September.
PAY ATTENTION: Install Pitch Football App for FREE to get the latest football News & Scores
Former Chelsea ace hails Super Eagles star who is January transfer target for Jose Mourinho
Meanwhile, Legit.ng reported that Paris Saint Germain forward, Kylian Mbappe, has thrown his weight behind Barcelona talisman Lionel Messi for the prestigious Ballon d'Or 2019 award.
The Argentine faces stiff competition from his close rival Cristiano Ronaldo and Liverpool's centre-back Virgil van Dijk.
Can Liverpool retain the Champions League Title | Legit TV


Title
Listen to your heart
1. Intent
- Purpose
To enable the audience to understand and explore the different types of Heart sounds and their reasons
- Audience
Curious minds, heart patients, people who keep track of their heartbeat conditions even at home.
Amateur Medical Practitioners, Community Health Workers
- Context
We depend a lot on our visit to the doctor to check with what's wrong with our body. Sometimes till the time we take a conscious effort in finding out
what's wrong with our heart it's too late. As people with layman level medical knowledge, a lot of us are unaware that a heartbeat sound can tell a lot more
than just the 'lub dub' rhythm and save our time from only waiting for the doctor to decode. People with pulmonary and cardiac problems could benefit much more
from this and keep track of their heart without paying so many medical visits and draining your mental energy.
- Trigger
What triggers me to take this topic is the thought that even though stethoscopes are open for the public to use, we still have problems understanding it. The auditory input isn't
cognitively decoded well. It'd be nice if we could visually see and understand the ongoings of the heart beats.
Question
How can I help educate my audience to visualize and decode the heartbeat sounds?
2. Data Abstraction
- Raw Data, Source
Type of data available - Heartbeat sounds, Types of Heartbeat sounds(normal, abnormal, unlabelled)
https://www.kaggle.com/kinguistics/heartbeat-sounds
- Clean Data
- Observations, Features, Sample
3. Task Abstraction
- What do you want the user to achieve
The ability to understand and explore the various sounds of heart beats and know when it's normal or abnormal.
4. Visual Design
- Three Sketches / Inspirations
- Exploration with Data
- Encoding Mapping
- Aesthetics Choices
- Selected Visual Design
5. Interaction Design
- Choice of Interaction
- Exploration
- Selected Interaction Design