Skip to main content

[JAI] Composing an OpImage from other OpImages

6 replies [Last post]
Anonymous

As posted earlier, I'm working on a Canny operator, and would like to
follow the JAI convention of presenting it as an OpImage.

This operation however, isn't 'atomic' like JAI's existing OpImages; it
involves a number of steps, each of which could be OpImages in their own
right - for example, the first step is Gaussian filtering, so I could
leverage JAI's existing convolution op. I cannot see any examples of an
OpImage that is composed of subordinate OpImages. What sort of approach
is suggested/recommended for nesting 'child' operations within a
'parent' operation?

Thanks,

Jason.

[att1.html]

Reply viewing options

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

OK, found the answer myself: PeriodicShiftOpImage is an example of this;
it has embedded TranslateIntOpImage's. Given that the JAI Ops have
package visibility, I guess I'll have to use JAI.create() within the
parent Ops to create the child ops.

J.

On Sun, 2005-05-29 at 10:45 +1000, Jason Grant wrote:

> As posted earlier, I'm working on a Canny operator, and would like to
> follow the JAI convention of presenting it as an OpImage.
>
> This operation however, isn't 'atomic' like JAI's existing OpImages;
> it involves a number of steps, each of which could be OpImages in
> their own right - for example, the first step is Gaussian filtering,
> so I could leverage JAI's existing convolution op. I cannot see any
> examples of an OpImage that is composed of subordinate OpImages. What
> sort of approach is suggested/recommended for nesting 'child'
> operations within a 'parent' operation?
>
> Thanks,
>
> Jason.
>
[att1.html]

Jason Grant

So I think the way to implement a "composed" ImageOp is to do something
like:

RenderedImage greyScaleImage = new CustomGreyscaleConverterOpImage
(inputImage...);
RenderedImage smoothedImage = new MyGaussianOpImage
(greyScaleImage, ...);
RenderedImage greyScaleImage = new CustomGreyscaleConverterOpImage
(smoothedImage...);
...
RenderedImage suppressedImage = new NonMaximalSuppressionOpImage
(lastStepImage...);

but I'm not sure where to put this logic. In the
ParentOpDescriptor.validateParameters() method seems like one option.

Once I've pushed this chained set of OpImages into my ParentOpImage as
it's source, it's not clear to me how I should set up the ParentOpImage
to do the work. Should ParentImageOp just subclass OpImage, and
delegate the following methods to the source OpImage:

computeTile(...)
mapSourceRect(...)
mapDestRect(...)

Or is there more to it than this? (e.g. if NonMaximalSuppressionOpImage
doesn't support tiling, does this approach break down?)

Also, will disposal of resources work out of the box, or should I be
careful to ensure that child ImageOps are disposed as soon as possible
to keep memory overheads down. If so, where should the child.dispose()
calls be made?

Thanks,

Jason.

On Sun, 2005-05-29 at 13:00 +1000, Jason Grant wrote:

> OK, found the answer myself: PeriodicShiftOpImage is an example of
> this; it has embedded TranslateIntOpImage's. Given that the JAI Ops
> have package visibility, I guess I'll have to use JAI.create() within
> the parent Ops to create the child ops.
>
> J.
>
> On Sun, 2005-05-29 at 10:45 +1000, Jason Grant wrote:
>
> > As posted earlier, I'm working on a Canny operator, and would like
> > to follow the JAI convention of presenting it as an OpImage.
> >
> > This operation however, isn't 'atomic' like JAI's existing OpImages;
> > it involves a number of steps, each of which could be OpImages in
> > their own right - for example, the first step is Gaussian filtering,
> > so I could leverage JAI's existing convolution op. I cannot see any
> > examples of an OpImage that is composed of subordinate OpImages.
> > What sort of approach is suggested/recommended for nesting 'child'
> > operations within a 'parent' operation?
> >
> > Thanks,
> >
> > Jason.
> >
[att1.html]

Jason Grant

I'm still not sure I'm on the right track, however my Canny OpImage now
simply delegates to it's [chained] source, in the same way that
StatisticsOpImage does. That seems to work well.

Regarding the chaining, I'm doing this in
CannyDescriptor.validateParameters as shown below. It is slow to
execute (minutes for a 2000x1700 digital photo), so I'm currently trying
to identify bottlenecks before I add the tracking algorithm which I
assume will be *really* slow. Maybe it has something to do with this
chaining strategy?

Another question right now is how to listen to the chained operation so
that I can keep a UI progress bar up to date so that the user is
encouraged that something is actually happening. I've tried using tile
listeners, but the listeners aren't notified of any events. I've also
tried using property listeners, again with nothing interesting coming
back - perhaps the recommended approach to add my own 'status'
properties during the execution of the Op, so that a listener can deduce
progress status?

Any tips appreciated.

------------------------------

protected static RenderedOp nonMaximalSuppression(RenderedOp src) {
ParameterBlockJAI pbj = new ParameterBlockJAI
("NonMaximalSuppression");
pbj.addSource(src);
pbj.setParameter("threshold", new Byte((byte)220));
return JAI.create("NonMaximalSuppression", pbj);
}

protected static RenderedOp chainOperations(RenderedOp source) {
RenderedOp greyConverter = convertToGray(source);
RenderedOp gaussian = convolveGaussian(greyConverter);
greyConverter.dispose();
RenderedOp gradientSector = gradientSector(gaussian);
gaussian.dispose();
RenderedOp nonMaximal = nonMaximalSuppression(gradientSector);
return nonMaximal;
}

protected boolean validateParameters(ParameterBlock args,
StringBuffer msg) {
...

// Put a series of OpImages ahead of us
RenderedOp source = (RenderedOp) args.getSource(0);
args.setSource(chainOperations(source), 0);
return true;
}

[att1.html]

Brian Burkhalter

On Tue, 31 May 2005, Jason Grant wrote:

> I'm still not sure I'm on the right track, however my Canny OpImage now
> simply delegates to it's [chained] source, in the same way that
> StatisticsOpImage does. That seems to work well.
>
> Regarding the chaining, I'm doing this in
> CannyDescriptor.validateParameters as shown below. It is slow to
> execute (minutes for a 2000x1700 digital photo), so I'm currently trying
> to identify bottlenecks before I add the tracking algorithm which I
> assume will be *really* slow. Maybe it has something to do with this
> chaining strategy?

I don't think that validateParameters() is the appropriate place to do
chaining. This method will be called when the operation node is added to the
graph. You want it to detect errors early. If you are chaining operations in
order to apply intrinsic JAI operations to other images before passing them to
your implementation then I would suggesting doing so perhaps in the OpImage
constructor or in the associated RenderedImageFactory (RIF).

> Another question right now is how to listen to the chained operation so
> that I can keep a UI progress bar up to date so that the user is
> encouraged that something is actually happening.

We don't really have any progress listener API in JAI. This is an open
enhancement request.

> I've tried using tile
> listeners, but the listeners aren't notified of any events.

If your tiles were small enough then TileComputationListeners might be able to
provide you with something meaningful.

Brian

> I've also
> tried using property listeners, again with nothing interesting coming
> back - perhaps the recommended approach to add my own 'status'
> properties during the execution of the Op, so that a listener can deduce
> progress status?
>
>
> Any tips appreciated.
>
> ------------------------------
>
> protected static RenderedOp nonMaximalSuppression(RenderedOp src) {
> ParameterBlockJAI pbj = new ParameterBlockJAI
> ("NonMaximalSuppression");
> pbj.addSource(src);
> pbj.setParameter("threshold", new Byte((byte)220));
> return JAI.create("NonMaximalSuppression", pbj);
> }
>
> protected static RenderedOp chainOperations(RenderedOp source) {
> RenderedOp greyConverter = convertToGray(source);
> RenderedOp gaussian = convolveGaussian(greyConverter);
> greyConverter.dispose();
> RenderedOp gradientSector = gradientSector(gaussian);
> gaussian.dispose();
> RenderedOp nonMaximal = nonMaximalSuppression(gradientSector);
> return nonMaximal;
> }
>
> protected boolean validateParameters(ParameterBlock args,
> StringBuffer msg) {
> ...
>
> // Put a series of OpImages ahead of us
> RenderedOp source = (RenderedOp) args.getSource(0);
> args.setSource(chainOperations(source), 0);
> return true;
> }
>
>
>

----------------
Brian Burkhalter
Advanced Development, Graphics and Media
Software Chief Technology Office
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

stefanposch
Offline
Joined: 2005-10-19

Hi,

I just found this old topic, as I was experimenting in the same direction a while ago.
I came up with the following solution, which seems to work. But I am not sure if its
along the JAI operator philosophy (which I do not really fully understand).

The example is derived from the SampleDescriptor.java as distrubited with jai-demo
for version 1.2 (I deleted comments and all imports) and implements just a closing operator.

What does everybody think of it?

Thanks Stefan

// derived from SampleDescriptor.java
//
// Stefan Posch, 13-Oct-2005

/**
* A single class that is both an OperationDescriptor and
* a RenderedImageFactory along with the one OpImage it is
* capable of creating.
*
* implements an closing on binary images, i.e. dilation with a subsequent erosion
*/

public class ClosingDescriptor extends OperationDescriptorImpl
implements RenderedImageFactory {
/**
* The resource strings that provide the general documentation and
* specify the parameter list for the "Closing" operation.
*/
private static final String[][] resources = {
{"GlobalName", "Closing"},
{"LocalName", "Closing"},
{"Vendor", "de.uni-halle.informatik"},
{"Description", "Closing"},
{"DocURL", "http://www.informatik.uni-halle.de/ClosingDescriptor.html"}, // a lie
{"Version", "1.0"},
{"arg0Desc", "kernel"}
};

/**
* The parameter names for the "Closing" operation. Extenders may
* want to rename them to something more meaningful.
*/
private static final String[] paramNames = {
"kernel"
};

/**
* The class types for the parameters of the "Closing" operation.
* User defined classes can be used here as long as the fully
* qualified name is used and the classes can be loaded.
*/
private static final Class[] paramClasses = {
javax.media.jai.KernelJAI.class
};

/**
* The default parameter values for the "Closing" operation
* when using a ParameterBlockJAI.
*/
private static final Object[] paramDefaults = {
null
};

/**
* The supported modes.
*/
private static final String[] supportedModes = {
"rendered"
};

/** Constructor. */
public ClosingDescriptor() {
super(resources, supportedModes, 1, paramNames, paramClasses,
paramDefaults, null);
}

/**
* Creates a ClosingOpImage with the given ParameterBlock if the
* ClosingOpImage can handle the particular ParameterBlock.
*/
public RenderedImage create(ParameterBlock paramBlock,
RenderingHints renderHints) {
if (!validateParameters(paramBlock)) {
return null;
}

RenderingHints hints = null;
// work around a bug - see tip im interest group by Aastha
// do not know yet, when to set this rendering hints
//if ( true )
// hints = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE);

ParameterBlock pb1 = new ParameterBlock();
pb1.addSource( paramBlock.getRenderedSource(0));
pb1.add( paramBlock.getObjectParameter(0));
RenderedOp dilatedIm = JAI.create( "Dilate", pb1, hints);

ParameterBlock pb2 = new ParameterBlock();
pb2.addSource( dilatedIm);
pb2.add( paramBlock.getObjectParameter(0));

return JAI.create( "Erode", pb2, hints);
}

/**
* Checks that all parameters in the ParameterBlock have the
* correct type before constructing the ClosingOpImage
*/
public boolean validateParameters(ParameterBlock paramBlock) {
Object arg = paramBlock.getObjectParameter(0);
if ( arg == null || !(arg instanceof KernelJAI) ) return false;
return true;
}
}

Brian Burkhalter

Hello. I do not see any problem with this after a brief perusal. There is one
way to improve it however. The validateParameters() method replicates the
functionality of this method:

http://download.java.net/media/jai/javadoc/1.1.3-beta/javax/media/jai/OperationDescriptorImpl.html#validateArguments(java.lang.String,%20java.awt.image.renderable.ParameterBlock,%20java.lang.StringBuffer)

Therefore I think you can eliminate your method and instead invoke this one.
Also, validateArguments() is called automatically by JAI.createNS() (which is
in turn invoked by JAI.create() on the default instance of the JAI class).
Therefore if you are always going to use JAI.create[NS]() to create your
operation you don't need to include the validation in the RIF create() method.

Brian

On Wed, 30 Aug 2006, jai-interest@javadesktop.org wrote:

> Hi,
>
> I just found this old topic, as I was experimenting in the same direction a while ago.
> I came up with the following solution, which seems to work. But I am not sure if its
> along the JAI operator philosophy (which I do not really fully understand).
>
> The example is derived from the SampleDescriptor.java as distrubited with jai-demo
> for version 1.2 (I deleted comments and all imports) and implements just a closing operator.
>
> What does everybody think of it?
>
> Thanks Stefan
>
>
> // derived from SampleDescriptor.java
> //
> // Stefan Posch, 13-Oct-2005
>
> /**
> * A single class that is both an OperationDescriptor and
> * a RenderedImageFactory along with the one OpImage it is
> * capable of creating.
> *
> * implements an closing on binary images, i.e. dilation with a subsequent erosion
> */
>
> public class ClosingDescriptor extends OperationDescriptorImpl
> implements RenderedImageFactory {
> /**
> * The resource strings that provide the general documentation and
> * specify the parameter list for the "Closing" operation.
> */
> private static final String[][] resources = {
> {"GlobalName", "Closing"},
> {"LocalName", "Closing"},
> {"Vendor", "de.uni-halle.informatik"},
> {"Description", "Closing"},
> {"DocURL", "http://www.informatik.uni-halle.de/ClosingDescriptor.html"}, // a lie
> {"Version", "1.0"},
> {"arg0Desc", "kernel"}
> };
>
> /**
> * The parameter names for the "Closing" operation. Extenders may
> * want to rename them to something more meaningful.
> */
> private static final String[] paramNames = {
> "kernel"
> };
>
>
>
> /**
> * The class types for the parameters of the "Closing" operation.
> * User defined classes can be used here as long as the fully
> * qualified name is used and the classes can be loaded.
> */
> private static final Class[] paramClasses = {
> javax.media.jai.KernelJAI.class
> };
>
> /**
> * The default parameter values for the "Closing" operation
> * when using a ParameterBlockJAI.
> */
> private static final Object[] paramDefaults = {
> null
> };
>
> /**
> * The supported modes.
> */
> private static final String[] supportedModes = {
> "rendered"
> };
>
> /** Constructor. */
> public ClosingDescriptor() {
> super(resources, supportedModes, 1, paramNames, paramClasses,
> paramDefaults, null);
> }
>
> /**
> * Creates a ClosingOpImage with the given ParameterBlock if the
> * ClosingOpImage can handle the particular ParameterBlock.
> */
> public RenderedImage create(ParameterBlock paramBlock,
> RenderingHints renderHints) {
> if (!validateParameters(paramBlock)) {
> return null;
> }
>
> RenderingHints hints = null;
> // work around a bug - see tip im interest group by Aastha
> // do not know yet, when to set this rendering hints
> //if ( true )
> // hints = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE);
>
> ParameterBlock pb1 = new ParameterBlock();
> pb1.addSource( paramBlock.getRenderedSource(0));
> pb1.add( paramBlock.getObjectParameter(0));
> RenderedOp dilatedIm = JAI.create( "Dilate", pb1, hints);
>
> ParameterBlock pb2 = new ParameterBlock();
> pb2.addSource( dilatedIm);
> pb2.add( paramBlock.getObjectParameter(0));
>
> return JAI.create( "Erode", pb2, hints);
> }
>
> /**
> * Checks that all parameters in the ParameterBlock have the
> * correct type before constructing the ClosingOpImage
> */
> public boolean validateParameters(ParameterBlock paramBlock) {
> Object arg = paramBlock.getObjectParameter(0);
> if ( arg == null || !(arg instanceof KernelJAI) ) return false;
> return true;
> }
> }
> [Message sent by forum member 'stefanposch' (stefanposch)]
>
> http://forums.java.net/jive/thread.jspa?messageID=148354
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
> For additional commands, e-mail: interest-help@jai.dev.java.net
>
>

----------------
Brian Burkhalter
Java Media, Imaging, and Graphics
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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