View2d
@Override
protected void setImage(DicomImageElement img) {
boolean newImg = img != null && !img.equals(imageLayer.getSourceImage());
if (newImg) {
deletePrLayers();
PrGraphicUtil.applyPresentationModel(img);
}
super.setImage(img);
updatePrButtonState(img, newImg);
if (newImg) {
updateKOselectedState(img);
}
}
DefaultView2d
protected void setImage(E img) {
boolean updateGraphics = false;
imageLayer.setEnableDispOperations(false);
if (img == null) {
actionsInView.put(ActionW.SPATIAL_UNIT.cmd(), Unit.PIXEL);
ActionState spUnitAction = eventManager.getAction(ActionW.SPATIAL_UNIT);
if (spUnitAction instanceof ComboItemListener) {
((ComboItemListener) spUnitAction)
.setSelectedItemWithoutTriggerAction(actionsInView.get(ActionW.SPATIAL_UNIT.cmd()));
}
// Force the update for null image
imageLayer.setEnableDispOperations(true);
imageLayer.setImage(null, null);
imageLayer.setEnableDispOperations(false);
setGraphicManager(new XmlGraphicModel());
closeLens();
} else {
E oldImage = imageLayer.getSourceImage();
if (img != null && !img.equals(oldImage)) {
updateGraphics = true;
actionsInView.put(ActionW.SPATIAL_UNIT.cmd(), img.getPixelSpacingUnit());
if (eventManager.getSelectedViewPane() == this) {
ActionState spUnitAction = eventManager.getAction(ActionW.SPATIAL_UNIT);
if (spUnitAction instanceof ComboItemListener) {
((ComboItemListener) spUnitAction)
.setSelectedItemWithoutTriggerAction(actionsInView.get(ActionW.SPATIAL_UNIT.cmd()));
}
}
actionsInView.put(ActionW.PREPROCESSING.cmd(), null);
ActionState spUnitAction = eventManager.getAction(ActionW.SPATIAL_UNIT);
if (spUnitAction instanceof ComboItemListener) {
((ComboItemListener) spUnitAction)
.setSelectedItemWithoutTriggerAction(actionsInView.get(ActionW.SPATIAL_UNIT.cmd()));
}
updateCanvas(img, false);
imageLayer.fireOpEvent(new ImageOpEvent(ImageOpEvent.OpEvent.ImageChange, series, img, null));
resetZoom();
// Update zoom operation to the current image (Reset update to the previous one)
ImageOpNode node = imageLayer.getDisplayOpManager().getNode(ZoomOp.OP_NAME);
if (node != null) {
double viewScale = getViewModel().getViewScale();
node.setParam(ZoomOp.P_RATIO_X, viewScale * img.getRescaleX());
node.setParam(ZoomOp.P_RATIO_Y, viewScale * img.getRescaleY());
}
imageLayer.setImage(img, (OpManager) actionsInView.get(ActionW.PREPROCESSING.cmd()));
if (AuditLog.LOGGER.isInfoEnabled()) {
PlanarImage image = img.getImage();
if (image != null) {
StringBuilder pixSize = new StringBuilder();
SampleModel sm = image.getSampleModel();
if (sm != null) {
int[] spsize = sm.getSampleSize();
if (spsize != null && spsize.length > 0) {
pixSize.append(spsize[0]);
for (int i = 1; i < spsize.length; i++) {
pixSize.append(',');
pixSize.append(spsize[i]);
}
}
}
AuditLog.LOGGER.info("open:image size:{},{} depth:{}", //$NON-NLS-1$
new Object[] { image.getWidth(), image.getHeight(), pixSize.toString() });
}
}
}
// Apply all image processing operation for visualization
imageLayer.setEnableDispOperations(true);
if (updateGraphics) {
GraphicModel modelList = (GraphicModel) img.getTagValue(TagW.PresentationModel);
// After getting a new image iterator, update the measurements
if (modelList == null) {
modelList = new XmlGraphicModel(img);
img.setTag(TagW.PresentationModel, modelList);
}
setGraphicManager(modelList);
}
if (panner != null) {
panner.updateImage();
}
if (lens != null) {
lens.updateImage();
lens.updateZoom();
}
}
}
RenderedImageLayer
@Override
public void setImage(E image, OpManager preprocessing) {
boolean init = (image != null && !image.equals(this.sourceImage)) || (image == null && sourceImage != null);
this.sourceImage = image;
this.preprocessing = preprocessing;
if (preprocessing != null || init) {
disOpManager.setFirstNode(getSourceRenderedImage());
updateDisplayOperations();
}
}
@Override
public RenderedImage getSourceRenderedImage() {
if (sourceImage != null) {
return sourceImage.getImage(preprocessing);
}
return null;
}
ImageElement
public PlanarImage getImage(OpManager manager) {
return getImage(manager, true);
}
public synchronized PlanarImage getImage(OpManager manager, boolean findMinMax) {
try {
return getCacheImage(startImageLoading(), manager, findMinMax);
} catch (OutOfMemoryError e1) {
/*
* Appends when loading a big image without tiling, the memory left is not enough for the renderedop (like
* Extrema)
*/
LOGGER.warn("Out of MemoryError: {}", this, e1); //$NON-NLS-1$
mCache.expungeStaleEntries();
System.gc();
System.gc();
try {
Thread.sleep(100);
} catch (InterruptedException et) {
// Do nothing
}
return getCacheImage(startImageLoading(), manager, findMinMax);
}
}
private PlanarImage startImageLoading() throws OutOfMemoryError {
PlanarImage cacheImage;
if ((cacheImage = mCache.get(this)) == null && readable && setAsLoading()) {
LOGGER.debug("Asking for reading image: {}", this); //$NON-NLS-1$
Load ref = new Load();
Future<PlanarImage> future = IMAGE_LOADER.submit(ref);
PlanarImage img = null;
try {
img = future.get();
} catch (InterruptedException e) {
// Re-assert the thread's interrupted status
Thread.currentThread().interrupt();
// We don't need the result, so cancel the task too
future.cancel(true);
} catch (ExecutionException e) {
if (e.getCause() instanceof OutOfMemoryError) {
setAsLoaded();
throw (OutOfMemoryError) e.getCause();
} else {
readable = false;
LOGGER.error("Cannot read pixel data!: {}", this, e); //$NON-NLS-1$
}
}
if (img != null) {
readable = true;
mCache.put(this, img);
cacheImage = img;
this.setTag(TagW.ImageCache, true);
}
setAsLoaded();
}
return cacheImage;
}
public static final ExecutorService IMAGE_LOADER = ThreadUtil.buildNewSingleThreadExecutor("Image Loader");
class Load implements Callable<PlanarImage> {
@Override
public PlanarImage call() throws Exception {
return loadImage();
}
}
protected PlanarImage loadImage() throws Exception {
return mediaIO.getImageFragment(this);
}
DicomMediaIO
@Override
public PlanarImage getImageFragment(MediaElement media) throws Exception {
if (media != null && media.getKey() instanceof Integer && isReadableDicom()) {
int frame = (Integer) media.getKey();
if (frame >= 0 && frame < numberOfFrame && hasPixel) {
// read as tiled rendered image
LOGGER.debug("Start reading dicom image frame: {} sopUID: {}", //$NON-NLS-1$
frame, TagD.getTagValue(this, Tag.SOPInstanceUID));
return getValidImage(readAsRenderedImage(frame, null), media);
}
}
return null;
}
@Override
public RenderedImage readAsRenderedImage(int frameIndex, ImageReadParam param) throws IOException {
readingImage = true;
try {
readMetaData(true);
checkIndex(frameIndex);
if (param == null) {
param = getDefaultReadParam();
}
RenderedImage bi;
if (decompressor != null) {
decompressor.setInput(iisOfFrame(frameIndex));
if (isRLELossless() && (pmi.isSubSambled() || pmi.name().startsWith("YBR"))) { //$NON-NLS-1$
bi = convertSubSambledAndYBR(frameIndex, param);
} else {
bi = decompressor.readAsRenderedImage(0, decompressParam(param));
}
} else {
// Rewrite image with subsampled model (otherwise cannot not be displayed as RenderedImage)
// Convert YBR_FULL into RBG as the ybr model is not well supported.
if (pmi.isSubSambled() || pmi.name().startsWith("YBR")) { //$NON-NLS-1$
bi = convertSubSambledAndYBR(frameIndex, param);
} else {
ImageReader reader = initRawImageReader();
bi = reader.readAsRenderedImage(frameIndex, param);
}
}
return validateSignedShortDataBuffer(bi);
} finally {
/*
* "readingImage = false" will close the stream of the tiled image. The problem is that
* readAsRenderedImage() do not read data immediately: RenderedImage delays the image reading
*/
}
}
private PlanarImage getValidImage(RenderedImage buffer, MediaElement media) {
PlanarImage img = null;
if (buffer != null) {
// Bug fix: CLibImageReader and J2KImageReaderCodecLib (imageio libs) do not handle negative values
// for short data. They convert signed short to unsigned short.
if (dataType == DataBuffer.TYPE_SHORT && buffer.getSampleModel().getDataType() == DataBuffer.TYPE_USHORT) {
img = RectifyUShortToShortDataDescriptor.create(buffer, LayoutUtil.createTiledLayoutHints(buffer));
} else if (ImageUtil.isBinary(buffer.getSampleModel())) {
ParameterBlock pb = new ParameterBlock();
pb.addSource(buffer);
// Tile size are set in this operation
img = JAI.create("formatbinary", pb, null); //$NON-NLS-1$
} else if (buffer.getTileWidth() != ImageFiler.TILESIZE || buffer.getTileHeight() != ImageFiler.TILESIZE) {
img = ImageFiler.tileImage(buffer);
} else {
img = NullDescriptor.create(buffer, LayoutUtil.createTiledLayoutHints(buffer));
}
/*
* Handle overlay in pixel data: extract the overlay, serialize it in a file and set all values to O in the
* pixel data.
*/
Integer overlayBitMask = (Integer) getTagValue(TagW.OverlayBitMask);
if (overlayBitMask != null) {
if (media.getTagValue(TagW.OverlayBurninDataPath) == null) {
// Serialize overlay (from pixel data)
Attributes ds = getDicomObject();
int[] embeddedOverlayGroupOffsets = Overlays.getEmbeddedOverlayGroupOffsets(ds);
if (embeddedOverlayGroupOffsets.length > 0) {
FileOutputStream fileOut = null;
ObjectOutput objOut = null;
try {
byte[][] overlayData = new byte[embeddedOverlayGroupOffsets.length][];
Raster raster = buffer.getData();
for (int i = 0; i < embeddedOverlayGroupOffsets.length; i++) {
overlayData[i] =
OverlayUtils.extractOverlay(embeddedOverlayGroupOffsets[i], raster, ds);
}
File file = File.createTempFile("ovly_", "", AppProperties.FILE_CACHE_DIR); //$NON-NLS-1$ //$NON-NLS-2$
fileOut = new FileOutputStream(file);
objOut = new ObjectOutputStream(fileOut);
objOut.writeObject(overlayData);
media.setTag(TagW.OverlayBurninDataPath, file.getPath());
} catch (Exception e) {
LOGGER.error("Cannot serialize overlay", e); //$NON-NLS-1$
} finally {
FileUtil.safeClose(objOut);
FileUtil.safeClose(fileOut);
}
}
}
// Set to 0 all bits outside bitStored
img = AndConstDescriptor.create(img, new int[] { overlayBitMask }, null);
}
img = DicomImageUtils.getRGBImageFromPaletteColorModel(img, getDicomObject());
}
return img;
}