Skip to main content

RE: [JAI-IMAGEIO] Getting filename to codec?

1 reply [Last post]
Anonymous

I'm debating whether this is off-topic, but I think not. We
all develop software that reads images using ImageIO, and this
is relevant. If you don't enjoy long-winded discussions of
software design, just skip this now. This is what makes me
love my job so I'm enthusiastic about it.

> All you need to do is implement a custom object
> (MultipleImageInput ?) that encapsulates both files. It can
> encapsulate it using a stream for each, byte[] for each, file
> for each, url for each, etc. If only one of the "files" is
> provided, in most cases you can find the other object, in
> other cases (e.g. the first is a byte[]) you will NEVER be
> able to find the second.

this is roughly how our original approach worked. however, it
has some shortcomings. the general problem is one of dependencies.
first, we want to keep our application independent of the details
of any particular plugin or image reader. second, we want to keep
the ImageReaders independent of the details of where the data
comes from. All we really need in the ImageReader is a stream to
each resource.

this raises the following two problems with your MultiImageInput:

1. some object has to know where all of the files/urls for a
particular format are located, in order to put them into the
MultiImageInput. this object necessarily has to know the details
of your format, which means that this object depends upon
format-specific details and thus can't be part of a general
software package. if you push this knowledge down into the
ImageReader, which is a plugin and thus is not necessarily part
of a general software package, then this dependency is broken.

Take for example the RPF format, which you can read about at
http://earth-info.nga.mil/publications/specs/printed/rpf/rpf.html
This format contains hundreds of files, which are all listed in
a format-specific "table of contents" file. Regardless of where
you choose to put the code that creates the list of files, it is
format-specific. Furthermore, just listing all of the files isn't
sufficient to read the image -- the ImageReader has to know the
significance of each file, which it can't determine from a filename
or anything.

2. in the case where you are not reading from Files, let's say
you're using HTTP URLs instead. you might be able to take an
initial URL and manipulate it to give you the names of the other
resources. That gives you a list of URLs instead of a list of
files.

Now the ImageReader has to know how to open URLs for reading.
If you want to be able to read Files too, then the ImageReader
has to be able to differentiate between URLs and Files, and
do the right thing in either case.

Then what happens if you write a new ImageReader that also wants
to handle the same decision (URLs vs. Files)? And what happens
if you add a new type of resource (database, for example) that
you need to be able to support in all of your existing readers?

So that's how the StreamFactory came to be. It abstracts the
calling program from the details of how many or what files the
ImageReader needs to do its job, and it abstracts the readers
from the details of what kind of resource it is dealing with.
It uses a SPI architecture so that new sources can be supported
without impacting the core libraries or the image readers.

> It is impossible to work with a standard ImageInputStream
> that was created by the default ImageInputStreamSpis since
> the ImageInputStream does not have any meta information on
> where the stream is located.

agreed. see below for more on this.

> You could however also implement the ImageInputStreamSpi and
> for certain input types (e.g. file and URL) probably
> construct an ImageInputStream where the first object is at
> offset 0 in the stream, and the second object is at offset
> (length of first object). This has problems in that you would
> need to make sure your Spi was always asked first since you
> are using standard input objects.

are you saying that the custom ImageInputStream created by your
hypothetical SPI would contain the bytes of each file concatenated
into a single byte[]? such a solution is not feasible simply
because of the potential size of images. Reading every one of
these related files into memory is simply unacceptable. Plus,
in the RPF format for example, you don't know all of the files
without reading data in a format-specific way.

> You will also have problems with some of the static read()
> methods in ImageIO though, since those create the stream from
> the source (e.g.
> url.getInputStream()) before asking any SPIs if they can read
> a URL. But it will work for others (e.g. ImageIO.read(File)
> ). You may be able to instanceof on the Stream and some
> casting, but this is going to fail in the arbitrary case.

Yes, we don't support ImageIO.read() for the formats that require
this extra functionality of finding multiple files. Since our
applications don't use this method, and we can live with not
supporting this for our API users, we are fine without it.
I don't see how you could support it anyway in this case --
to reiterate, some piece of code SOMEWHERE has to know the
format specifics to know what files are needed to open the
image. And if you're using the ImageInputStreamSpi, you don't
know anything format specific. You can't make one
ImageInputStreamSpi for each of your supported formats, if
only because the API chooses one SPI over another based only
on the java class. So you can't have two different format SPIs
both accept String or File, since the ImageIO.createImageInputStream()
will always choose the same one.

(This is actually an argument for a chain of responsibility type
pattern in ImageIO -- where each Spi has a method like canDecodeInput(),
perhaps acceptInput(Object) that lets it accept or decline to provide
an ImageInputStream for a given input. This gets you at least part
of the way there, but it just isn't in the API now and couldn't
possibly get in before Java 6, which is impossible to rely on.)

My basic point is that yes, the price of flexibility is software
that is more complex. That is a basic tradeoff of software
design, and in my case I tend to err on the side of flexibility.
That's why the command-line programs I've created in the past
tend to have two dozen extra options, even though no one ever
uses them. ;-)

> In general, this seems like a bad image format design. All
> related files should be appended into a single file with an
> index structure at the start. Far easier to work with and
> less chance for corruption.

to some degree i agree. i don't make the image formats, i just
read them. ;-)

but on the other hand, a single huge file can be problematic
where a handful of smaller files might not be. take, for example,
the Landsat format (another case of the "associated files" problem).
this multispectral format places each band in a separate file.
some info here: http://earth.esa.int/services/esa_doc/doc_tpm.html

in this format, you might want to independently read only, say,
bands 1 3 and 4 (or some other arbitrary band combination). placing
them in separate files could save on data transmission/storage.
the same is even more true when dealing with hyperspectral imagery.

those are my thoughts.

Mike

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Robert Engels

I think you are missing some things:

As far as the concatenated stream, you don't need to read all of the files
at once. You know where the files start and maintain pointers (and seek) to
read the needed data depending on which file (or portion thereof) is needed.
The ImageReader would need to know the internal format though.

But more importantly, there are natural dependencies that you cannot avoid.
If an "image" is made up of multiple files, and each file is may or may not
be valid on its own, then you have a built-in dependency between the files.
Since you have dependency between the "files", you now are a functional
dependency - given one file you must be able to find the other related
files. This may or may not be possible given the other features of the OS,
protocols, etc. Since you have this functional dependency it must be
handled. The simplest (and I think best) solution is using MultiImageInput.
It can do "whatever" is needed to find the other files and create "input
streams" from them.

I would create the following interface:

interface ImageStreamFinder {
ImageInputStream open(String basename,String requested);
}

Then MultiImageInput has the following ctors

MultiImageInput(File baseFile); // convenience method
MultiImageInput(URL baseUrl); // convenience method
MultiImageInput(ImageStreamFinder isf,String basename);

It is trivial to create ImageStreamFinder implementations for both Files and
Urls. It would also be trivial to create a ImageStreamFinder for
concatenated byte[], given the lengths of each array. You could also create
ones that read different BLOBs from a db based upon the name., etc.

Then just create your ImageReaderSpi that creates your ImageReader when the
input is a MultiImageInput.

You could also create a factory to register different ImageStreamFinder
implementations, but because of the highly coupled nature I don't see the
value.

If the files that make up the "whole image" are themselves standalone
images, the ImageReader could becomes trivial, since you can create an
ImageReader for each standard ImageInputStream returned by the
ImageStreamFinder.

-----Original Message-----
From: Nidel, Mike [mailto:mike.nidel@lmco.com]
Sent: Friday, June 02, 2006 11:39 AM
To: interest@jai-imageio.dev.java.net
Subject: RE: [JAI-IMAGEIO] Getting filename to codec?

I'm debating whether this is off-topic, but I think not. We all develop
software that reads images using ImageIO, and this is relevant. If you don't
enjoy long-winded discussions of software design, just skip this now. This
is what makes me love my job so I'm enthusiastic about it.

> All you need to do is implement a custom object (MultipleImageInput ?)
> that encapsulates both files. It can encapsulate it using a stream for
> each, byte[] for each, file for each, url for each, etc. If only one
> of the "files" is provided, in most cases you can find the other
> object, in other cases (e.g. the first is a byte[]) you will NEVER be
> able to find the second.

this is roughly how our original approach worked. however, it has some
shortcomings. the general problem is one of dependencies.
first, we want to keep our application independent of the details of any
particular plugin or image reader. second, we want to keep the ImageReaders
independent of the details of where the data comes from. All we really need
in the ImageReader is a stream to each resource.

this raises the following two problems with your MultiImageInput:

1. some object has to know where all of the files/urls for a particular
format are located, in order to put them into the MultiImageInput. this
object necessarily has to know the details of your format, which means that
this object depends upon format-specific details and thus can't be part of a
general software package. if you push this knowledge down into the
ImageReader, which is a plugin and thus is not necessarily part of a general
software package, then this dependency is broken.

Take for example the RPF format, which you can read about at
http://earth-info.nga.mil/publications/specs/printed/rpf/rpf.html
This format contains hundreds of files, which are all listed in a
format-specific "table of contents" file. Regardless of where you choose to
put the code that creates the list of files, it is format-specific.
Furthermore, just listing all of the files isn't sufficient to read the
image -- the ImageReader has to know the significance of each file, which it
can't determine from a filename or anything.

2. in the case where you are not reading from Files, let's say you're using
HTTP URLs instead. you might be able to take an initial URL and manipulate
it to give you the names of the other resources. That gives you a list of
URLs instead of a list of files.

Now the ImageReader has to know how to open URLs for reading.
If you want to be able to read Files too, then the ImageReader has to be
able to differentiate between URLs and Files, and do the right thing in
either case.

Then what happens if you write a new ImageReader that also wants to handle
the same decision (URLs vs. Files)? And what happens if you add a new type
of resource (database, for example) that you need to be able to support in
all of your existing readers?

So that's how the StreamFactory came to be. It abstracts the calling program
from the details of how many or what files the ImageReader needs to do its
job, and it abstracts the readers from the details of what kind of resource
it is dealing with.
It uses a SPI architecture so that new sources can be supported without
impacting the core libraries or the image readers.

> It is impossible to work with a standard ImageInputStream that was
> created by the default ImageInputStreamSpis since the ImageInputStream
> does not have any meta information on where the stream is located.

agreed. see below for more on this.

> You could however also implement the ImageInputStreamSpi and for
> certain input types (e.g. file and URL) probably construct an
> ImageInputStream where the first object is at offset 0 in the stream,
> and the second object is at offset (length of first object). This has
> problems in that you would need to make sure your Spi was always asked
> first since you are using standard input objects.

are you saying that the custom ImageInputStream created by your hypothetical
SPI would contain the bytes of each file concatenated into a single byte[]?
such a solution is not feasible simply because of the potential size of
images. Reading every one of these related files into memory is simply
unacceptable. Plus, in the RPF format for example, you don't know all of the
files without reading data in a format-specific way.

> You will also have problems with some of the static read() methods in
> ImageIO though, since those create the stream from the source (e.g.
> url.getInputStream()) before asking any SPIs if they can read a URL.
> But it will work for others (e.g. ImageIO.read(File) ). You may be
> able to instanceof on the Stream and some casting, but this is going
> to fail in the arbitrary case.

Yes, we don't support ImageIO.read() for the formats that require this extra
functionality of finding multiple files. Since our applications don't use
this method, and we can live with not supporting this for our API users, we
are fine without it.
I don't see how you could support it anyway in this case -- to reiterate,
some piece of code SOMEWHERE has to know the format specifics to know what
files are needed to open the image. And if you're using the
ImageInputStreamSpi, you don't know anything format specific. You can't make
one ImageInputStreamSpi for each of your supported formats, if only because
the API chooses one SPI over another based only on the java class. So you
can't have two different format SPIs both accept String or File, since the
ImageIO.createImageInputStream() will always choose the same one.

(This is actually an argument for a chain of responsibility type pattern in
ImageIO -- where each Spi has a method like canDecodeInput(), perhaps
acceptInput(Object) that lets it accept or decline to provide an
ImageInputStream for a given input. This gets you at least part of the way
there, but it just isn't in the API now and couldn't possibly get in before
Java 6, which is impossible to rely on.)

My basic point is that yes, the price of flexibility is software that is
more complex. That is a basic tradeoff of software design, and in my case I
tend to err on the side of flexibility.
That's why the command-line programs I've created in the past tend to have
two dozen extra options, even though no one ever uses them. ;-)

> In general, this seems like a bad image format design. All related
> files should be appended into a single file with an index structure at
> the start. Far easier to work with and less chance for corruption.

to some degree i agree. i don't make the image formats, i just read them.
;-)

but on the other hand, a single huge file can be problematic where a handful
of smaller files might not be. take, for example, the Landsat format
(another case of the "associated files" problem).
this multispectral format places each band in a separate file.
some info here: http://earth.esa.int/services/esa_doc/doc_tpm.html

in this format, you might want to independently read only, say, bands 1 3
and 4 (or some other arbitrary band combination). placing them in separate
files could save on data transmission/storage.
the same is even more true when dealing with hyperspectral imagery.

those are my thoughts.

Mike

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai-imageio.dev.java.net
For additional commands, e-mail: interest-help@jai-imageio.dev.java.net