Skip to main content

[ SOLVED ]TIFF : How to insert empty and add pixels subsequently `?

9 replies [Last post]
broumbroum
Offline
Joined: 2010-04-24

I'm facing an issue where it may be possible to make tiff pictures in a low-memory cost fashion .
there it is : open a file for writing a sequence, insert an empty image (so called primary ifd), add one thumbnail and fill out the process by adding pixels with replacePixels before to quit with endsequence.
(UNIQUE source : exif structure)

here I've been uneasy with the IFD header, which seems no to be recognized by the tiffImagereader.readHeader()....
Does someone know where pitfalls are for such picture I/O ops ?

I write subsequently tiled regions of the source pic and i get this error :

java.lang.IllegalArgumentException: Forward mapped source region does not intersect destination region!<br />
        at com.sun.media.imageioimpl.plugins.tiff.TIFFImageWriter.replacePixels(TIFFImageWriter.java:3420)

I've looked at the tiffimagewriter source code and it's rather complex stuff.... I don't know if both Imagewriteparam must be set when using replacepixels (setdestinationoffset() ??)

iwp.setDestinationOffset(dstROIB.getLocation()); // is that necessary ?<br />
                                                w.prepareReplacePixels(0, dstROIB); // seems to be involved with destinationoffset for intersection<br />
                                                Rectangle dstBufferROI = new Rectangle(dstBuffer.getWidth(), dstBuffer.getHeight()).intersection(dstROIB);<br />
                                                w.replacePixels(dstBuffer.getSubimage(dstBufferROI.x, dstBufferROI.y, dstBufferROI.width, dstBufferROI.height), iwp);<br />
                                                w.endReplacePixels();<br />
                                                dstBuffer.flush();

Message was edited by: broumbroum

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
broumbroum
Offline
Joined: 2010-04-24

SOLVED
> Yet another issue : the picture seems slightly
> "brighter" than original (taken from a jpg camera).
> Need to fix this...
>
> Message was edited by: broumbroum

PhotometricInterpretation has to be set explicitely to RGB for lesser byte-conversion within I/O ops. That is :
[code]
tifd.removeTIFFField(ttagPhoto.getNumber());
tifd.addTIFFField(new TIFFField(ttagPhoto, BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB));
[/code]

broumbroum
Offline
Joined: 2010-04-24

afaik, currently no thumbnail support.
then [code]w.writeToSequence(new IIOImage(thumbnails.firstElement(), null, _getIIOMforDefaultImage(w, _getITS(thumbnails.firstElement()), iwp)), iwp);
[/code] can be /*commented*/ out.

broumbroum
Offline
Joined: 2010-04-24

So I added setsourceRegion() to imagewriteparam :
[code]
iwp.setDestinationOffset(dstROIB.getLocation());
w.prepareReplacePixels(0, dstROIB);
Rectangle dstBufferROI = new Rectangle(dstBuffer.getWidth(), dstBuffer.getHeight()).intersection(dstROIB);
[b] iwp.setSourceRegion(dstBufferROI);[/b]
w.replacePixels(dstBuffer, iwp);
w.endReplacePixels();
dstBuffer.flush();[/code]

and I get after end of the write sequence, on the next read [code]javax.imageio.IIOException: I/O error reading header!
at com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader.readHeader(TIFFImageReader.java:224)
at com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader.locateImage(TIFFImageReader.java:231)
at com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader.seekToImage(TIFFImageReader.java:305)
at com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader.getWidth(TIFFImageReader.java:512)[/code]

Message was edited by: broumbroum

Message was edited by: broumbroum

broumbroum
Offline
Joined: 2010-04-24

Resolved :
My [b]ImageOuputStream wasn't correctly flushed and closed [/b]after the writes.
However, iwp.setDest and iwp.setSource have to be set in order to match the specified replacePixel region (though looks like a bit strange doing so, ...).[/b][b]

broumbroum
Offline
Joined: 2010-04-24

Now that I've updated the code with (hopefully!) correct close() and flush() I can write w/o errors by replacing pixels, tile by tile.
[code]ImageWriter w = getWriter(0, storeMime);
ImageWriteParam iwp = _getIWP(w, dim, storeMime, _type);
w = __getOutput(output, w, false);
ImageTypeSpecifier its = (renderMode & MODE_TILE) != 0 ? _getITS(_type) : _getITS((RenderedImage) data);
IIOMetadata meta = metadata instanceof IIOMetadata ? _getIIOMforImageType(w, metadata, its, iwp) : _getIIOMforDefaultImage(w, its, iwp);
IIOImage iioimage = null;
IIOMetadata streamMetadata = null;
if ((renderMode & MODE_TILE) != 0) {
ImageReader r = getReader(0, tileMime);
r = __getInput(tileSrc, innerResource, r, false);
streamMetadata = r.getStreamMetadata();
boolean doReplace = true;
if (doReplace = w.canWriteSequence()) {
Dimension numTiles = getNumTilesXY(r);
Dimension tiles = _WRITE_TILES_DIMENSION;
w.prepareWriteSequence(_getIIOMforStream(w, streamMetadata, iwp));
if (doReplace = w.canInsertEmpty(-1)) {
w.prepareInsertEmpty(-1, its, dim.width, dim.height, meta,
null, /* thumbnail (has to be appended after IFD) */
iwp);
w.endInsertEmpty();
if (doReplace = w.canReplacePixels(0)) {
Vector thumbnails = _getThumbnails(r, _MIN_THUMBNAILS_SIZE, _type);
if (!thumbnails.isEmpty()) {
w.writeToSequence(new IIOImage(thumbnails.firstElement(), null, _getIIOMforDefaultImage(w, _getITS(thumbnails.firstElement()), iwp)), iwp);
}
for (int y = 0; y
< numTiles.height; y++) {
for (int x = 0; x
< numTiles.width; x++) {
ImageReadParam irp = _getIRP(r, _type);
Rectangle sroi = new Rectangle(x * tiles.width, y * tiles.height, tiles.width, tiles.height).intersection(new Rectangle(dim));
if (sroi.isEmpty()) {
continue;
}
irp.setSourceRegion(sroi);
BufferedImage b = r.read(0, irp);
if (b.getType() != _type) {
b = convertToBuffered(b, obs, true);
}
trackImage(trackerPty, b);
waitFor(trackerPty);
Rectangle replace = new Rectangle(x * tiles.width, y * tiles.height, tiles.width, tiles.height).intersection(new Rectangle(dim));
iwp.setDestinationOffset(replace.getLocation());
//follow down to next post...[/code]

Message was edited by: broumbroum

broumbroum
Offline
Joined: 2010-04-24

[code]w.prepareReplacePixels(0, replace);
w.replacePixels(b, iwp);
w.endReplacePixels();
b.flush();
}
}
}
}
w.endWriteSequence();
((ImageOutputStream) w.getOutput()).flush();
((ImageOutputStream) w.getOutput()).close();
w.dispose();[/code]
But then, as I look read the picture I get the following error from tiff reader (throughout an IIIOReadProgressListener ) :
[code]Read... (tif).(tif) REFERENCE_BLACK_WHITE not found, assuming 0-255/128-255/128-255
.(tif) REFERENCE_BLACK_WHITE not found, assuming 0-255/128-255/128-255
and so on.... for a whole 2400 lines area.
[/code]
Looked at the source of TiffImageWriter and TIffCompressor, the REFERENCE_BLACK_WHITE is not written out when using no compression at all (which is needed to use the replacePixels() capability) . I Think about posting a bug ... using JIIO 1.1

Any suggest ?

source

Message was edited by: broumbroum

Message was edited by: broumbroum

broumbroum
Offline
Joined: 2010-04-24

ok

Message was edited by: broumbroum

broumbroum
Offline
Joined: 2010-04-24

SOLVED this warning message with :
[code]public static IIOMetadata _getIIOMforImageType(ImageWriter w, IIOMetadata metadata, ImageTypeSpecifier its, ImageWriteParam iwp) {
if (metadata == null) {
metadata = w.getDefaultImageMetadata(its, iwp);
} else {
metadata = w.convertImageMetadata(metadata, its, iwp);
}
if (w instanceof TIFFImageWriter) {
/** null compressor fix for TIFFImageWriter */
TIFFTag ttagRefBW = BaselineTIFFTagSet.getInstance().getTag(BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE);
TIFFTag ttagPhoto = BaselineTIFFTagSet.getInstance().getTag(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
TIFFDirectory tifd;
try {
tifd = TIFFDirectory.createFromMetadata(metadata);
if (tifd.containsTIFFField(ttagPhoto.getNumber())) {
int photometric = tifd.getTIFFField(ttagPhoto.getNumber()).getAsInt(0);
float[] referenceBlackWhite;
switch (photometric) {
case BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB:
referenceBlackWhite = _useTIFF_NOHead_NOFoot ? referenceBlackWhiteRGB_NOHead_NOFoot : referenceBlackWhiteRGB;
break;
case BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR:
referenceBlackWhite = _useTIFF_NOHead_NOFoot ? referenceBlackWhiteYCbCr_NOHead_NOFoot : referenceBlackWhiteYCbCr;
break;
default:
referenceBlackWhite = null;
break;
}
if (referenceBlackWhite == null) {
tifd.removeTIFFField(ttagRefBW.getNumber());
} else {
tifd.addTIFFField(new TIFFField(ttagRefBW, ttagRefBW.TIFF_FLOAT, 6, referenceBlackWhite));
if (JXAenvUtils._debug) {
System.out.print("TIFFField ReferenceBlackWhite ");
for (float f : referenceBlackWhite) {
System.out.print(" " + f);
}
System.out.println();
}
}
} finally{
return metadata;
}
}
}
[/code]
Finally bufferedType must be one of the --three (3) components BufferedImage.TYPE_...-- e.g. TYPE_3BYTE_BGR or TYPE_INT_RGB

Yet another issue : the picture seems slightly "brighter" than original (taken from a jpg camera). Need to fix this...

Message was edited by: broumbroum

broumbroum
Offline
Joined: 2010-04-24

here are references :
[code]
/**YCbCr - no headroom/no footroom*/
final static float[] referenceBlackWhiteYCbCr_NOHead_NOFoot = new float[]{0, 255, 128, 255, 128, 255};
/**YCbCr - CCIR Recommendation 601.1 headroom/footroom*/
final static float[] referenceBlackWhiteYCbCr = new float[]{15, 235, 128, 240, 128, 240};
/**RGB - no headroom/no footroom*/
final static float[] referenceBlackWhiteRGB_NOHead_NOFoot = new float[]{0, 255, 0, 255, 0, 255};
/**RGB - CCIR Recommendation 601.1 headroom/footroom*/
final static float[] referenceBlackWhiteRGB = new float[]{16, 235, 16, 235, 16, 235};[/code]

Message was edited by: broumbroum