/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.acquire.explorer.dicom;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.io.File;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import javax.media.jai.PlanarImage;
import javax.media.jai.operator.TranslateDescriptor;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.VR;
import org.dcm4che3.util.UIDUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.weasis.acquire.explorer.AcquireImageInfo;
import org.weasis.acquire.explorer.AcquireManager;
import org.weasis.core.api.image.CropOp;
import org.weasis.core.api.image.RotationOp;
import org.weasis.core.api.image.SimpleOpManager;
import org.weasis.core.api.image.util.ImageFiler;
import org.weasis.core.api.image.util.Unit;
import org.weasis.core.api.media.data.ImageElement;
import org.weasis.core.api.media.data.TagW;
import org.weasis.core.api.media.data.Tagable;
import org.weasis.core.ui.model.GraphicModel;
import org.weasis.dicom.codec.TagD;
import org.weasis.dicom.codec.TagD.Level;
import org.weasis.dicom.codec.utils.DicomMediaUtils;
import org.weasis.dicom.explorer.pr.PrSerializer;
import org.weasis.dicom.tool.Dicomizer;
public final class Transform2Dicom {
private static final Logger LOGGER = LoggerFactory.getLogger(Transform2Dicom.class);
private Transform2Dicom() {
}
/**
* Do the encoding of the given image in a standard lossy JPEG format with optionally doing some pre-processing
* operations (like resize, flip, crop, zoom, contrast ...) if any postProcessOperation have been set in the
* AcquireImageInfo. The temporary image file is then encapsulated in a standard DICOM format according to the
* proper Dicom attributes set in the AcquireImageInfo. This Dicom is written in the exportDirDicom with its
* sopInstanceUID as filename.
*
*
* @param imageInfo
* @param exportDirDicom
* @param exportDirImage
* @param seriesInstanceUID
* Global series for all PR
* @return
*/
public static boolean dicomize(AcquireImageInfo imageInfo, File exportDirDicom, File exportDirImage,
String seriesInstanceUID) {
ImageElement imageElement = imageInfo.getImage();
String sopInstanceUID = Objects.requireNonNull((String) imageElement.getTagValue(TagD.getUID(Level.INSTANCE)));
// Transform to JPEG
File imgFile = imageElement.getFileCache().getOriginalFile().get();
if (imgFile == null || !imageElement.getMimeType().contains("jpg") //$NON-NLS-1$
|| !imageInfo.getCurrentValues().equals(imageInfo.getDefaultValues())) {
imgFile = new File(exportDirImage, sopInstanceUID + ".jpg"); //$NON-NLS-1$
SimpleOpManager opManager = imageInfo.getPostProcessOpManager();
PlanarImage transformedImage = imageElement.getImage(opManager, false);
Rectangle area = (Rectangle) opManager.getParamValue(CropOp.OP_NAME, CropOp.P_AREA);
Integer rotationAngle = Optional.ofNullable((Integer) opManager.getParamValue(RotationOp.OP_NAME, RotationOp.P_ROTATE)).orElse(0);
rotationAngle = rotationAngle % 360;
if (area != null && rotationAngle != 0 && rotationAngle != 180) {
transformedImage = TranslateDescriptor.create(transformedImage, (float) -area.getX(),
(float) -area.getY(), null, null);
}
if (!ImageFiler.writeJPG(imgFile, transformedImage, 0.8f)) {
// out of memory ??
imgFile.delete();
LOGGER.error("Cannot Transform to jpeg {}", imageElement.getName()); //$NON-NLS-1$
return false;
}
}
// Dicomize
if (imgFile.canRead()) {
Attributes attrs = imageInfo.getAttributes();
DicomMediaUtils.fillAttributes(AcquireManager.GLOBAL.getTagEntrySetIterator(), attrs);
DicomMediaUtils.fillAttributes(imageInfo.getSeries().getTagEntrySetIterator(), attrs);
DicomMediaUtils.fillAttributes(imageElement.getTagEntrySetIterator(), attrs);
// Spatial calibration
if (Unit.PIXEL != imageElement.getPixelSpacingUnit()) {
attrs.setString(Tag.PixelSpacingCalibrationDescription, VR.LO, "Used fiducial"); //$NON-NLS-1$
double unitRatio = imageElement.getPixelSize()
* Unit.MILLIMETER.getConversionRatio(imageElement.getPixelSpacingUnit().getConvFactor());
attrs.setDouble(Tag.PixelSpacing, VR.DS, unitRatio, unitRatio);
}
try {
Dicomizer.jpeg(attrs, imgFile, new File(exportDirDicom, sopInstanceUID), false);
} catch (Exception e) {
LOGGER.error("Cannot Dicomize {}", imageElement.getName(), e); //$NON-NLS-1$
return false;
}
// Presentation State
GraphicModel grModel = (GraphicModel) imageElement.getTagValue(TagW.PresentationModel);
if (grModel != null && grModel.hasSerializableGraphics()) {
Point2D offset = null;
Rectangle crop =
(Rectangle) imageInfo.getPostProcessOpManager().getParamValue(CropOp.OP_NAME, CropOp.P_AREA);
if (crop != null) {
Integer rotationAngle = Optional.ofNullable((Integer) imageInfo.getPostProcessOpManager().getParamValue(RotationOp.OP_NAME, RotationOp.P_ROTATE)).orElse(0);
rotationAngle = rotationAngle % 360;
if (rotationAngle == 0 || rotationAngle == 180) {
offset = new Point2D.Double(crop.getX(), crop.getY());
} else {
double factor = 2.0; // work only with 90 and 270 degrees
offset = new Point2D.Double(crop.getX() * factor, crop.getY() * factor);
}
}
String prUid = UIDUtils.createUID();
File outputFile = new File(exportDirDicom, prUid);
PrSerializer.writePresentation(grModel, attrs, outputFile, seriesInstanceUID, prUid, offset);
}
} else {
LOGGER.error("Cannot read JPEG image {}", imageElement.getName()); //$NON-NLS-1$
return false;
}
return true;
}
/**
* Populates Date and Time for all Attributes in the imageInfo Collection with respect to the youngest. That is :
* the first image content Date and Time would define the SerieDate and SerieTime within the current Serie, and so
* on within the current Study
*
* @param collection
* @param dicomTags
*/
public static void buildStudySeriesDate(Collection<AcquireImageInfo> collection, final Tagable dicomTags) {
TagW seriesDate = TagD.get(Tag.SeriesDate);
TagW seriesTime = TagD.get(Tag.SeriesTime);
TagW studyDate = TagD.get(Tag.StudyDate);
TagW studyTime = TagD.get(Tag.StudyTime);
// Reset study and series values
dicomTags.setTag(studyDate, null);
dicomTags.setTag(studyTime, null);
collection.forEach(i -> {
i.getSeries().setTag(seriesDate, null);
i.getSeries().setTag(seriesTime, null);
});
for (AcquireImageInfo imageInfo : collection) {
ImageElement imageElement = imageInfo.getImage();
LocalDateTime date = TagD.dateTime(Tag.ContentDate, Tag.ContentTime, imageElement);
if (date == null) {
continue;
}
LocalDateTime minSeries = TagD.dateTime(Tag.SeriesDate, Tag.SeriesTime, imageInfo.getSeries());
if (minSeries == null || date.isBefore(minSeries)) {
imageInfo.getSeries().setTag(seriesDate, date.toLocalDate());
imageInfo.getSeries().setTag(seriesTime, date.toLocalTime());
}
LocalDateTime minStudy = TagD.dateTime(Tag.StudyDate, Tag.StudyTime, dicomTags);
if (minStudy == null || date.isBefore(minStudy)) {
dicomTags.setTag(studyDate, date.toLocalDate());
dicomTags.setTag(studyTime, date.toLocalTime());
}
}
}
}