Mercurial > hg > orthanc-imagej
view com/orthancserver/DicomDecoder.java @ 28:97a1d882bdc1 default tip
added CITATION.cff
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sat, 06 Apr 2024 17:17:26 +0200 |
parents | abd670eaee8c |
children |
line wrap: on
line source
/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium * Copyright (C) 2017-2021 Osimis S.A., Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ package com.orthancserver; import ij.ImagePlus; import ij.ImageStack; import ij.process.ImageProcessor; import ij.process.ShortProcessor; import ij.process.ColorProcessor; import ij.io.FileInfo; import ij.measure.Calibration; import org.json.simple.*; import java.util.List; import java.util.ArrayList; import java.util.Collections; import java.io.IOException; import javax.swing.SwingWorker; import java.util.concurrent.ExecutionException; import javax.swing.JFrame; import javax.swing.JProgressBar; import java.awt.BorderLayout; import javax.swing.JPanel; import javax.swing.JButton; import java.awt.FlowLayout; import javax.swing.border.EmptyBorder; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class DicomDecoder { private static class ProgressDialog extends JFrame { private JProgressBar bar_ = new JProgressBar(); private boolean canceled_ = false; public ProgressDialog(int count) { getContentPane().setLayout(new BorderLayout()); bar_.setBorder(new EmptyBorder(20, 20, 20, 20)); getContentPane().add(bar_, BorderLayout.NORTH); JPanel buttonPane = new JPanel(); buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); getContentPane().add(buttonPane, BorderLayout.SOUTH); JButton cancelButton = new JButton("Cancel"); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg) { canceled_ = true; } }); buttonPane.add(cancelButton); bar_.setMaximum(0); bar_.setMaximum(count); setSize(500, 150); setTitle("Importing series from Orthanc"); setLocationRelativeTo(null); // Center dialog on screen } public void SetProgress(int value) { bar_.setValue(value); } public boolean IsCanceled() { return canceled_; } }; private static void ExtractCalibration(ImagePlus image, JSONObject tags) { JSONObject rescaleIntercept = (JSONObject) tags.get("0028,1052"); JSONObject rescaleSlope = (JSONObject) tags.get("0028,1053"); if (rescaleIntercept != null && rescaleSlope != null) { double[] coeff = { Float.valueOf((String) rescaleIntercept.get("Value")), Float.valueOf((String) rescaleSlope.get("Value")) }; image.getCalibration().setFunction(Calibration.STRAIGHT_LINE, coeff, "Gray Value"); } } private static void ExtractPixelSpacing(ImagePlus image, JSONObject tags) { JSONObject pixelSpacing = (JSONObject) tags.get("0028,0030"); if (pixelSpacing != null) { String[] tokens = ((String) pixelSpacing.get("Value")).split("\\\\"); if (tokens.length == 2) { FileInfo fi = image.getFileInfo(); fi.pixelWidth = Float.valueOf(tokens[0]); fi.pixelHeight = Float.valueOf(tokens[1]); fi.unit = "mm"; image.setFileInfo(fi); image.getCalibration().pixelWidth = fi.pixelWidth; image.getCalibration().pixelHeight = fi.pixelHeight; image.getCalibration().setUnit(fi.unit); } } } private static void ExtractDicomInfo(ImagePlus image, JSONObject tags) { String info = new String(); ArrayList<String> tagsIndex = new ArrayList<String>(); for (Object tag : tags.keySet()) { tagsIndex.add((String) tag); } Collections.sort(tagsIndex); for (String tag : tagsIndex) { JSONObject value = (JSONObject) tags.get(tag); if (((String) value.get("Type")).equals("String")) { info += (tag + " " + (String) value.get("Name") + ": " + (String) value.get("Value") + "\n"); } } image.setProperty("Info", info); } private static ImageProcessor DecodeInstance(OrthancConnection c, String uuid) throws IOException { try { String uri = "/instances/" + uuid + "/image-uint16"; ShortProcessor slice = new ShortProcessor(c.ReadImage(uri)); return slice; } catch (IllegalArgumentException e) { // Color image String uri = "/instances/" + uuid + "/preview"; ColorProcessor slice = new ColorProcessor(c.ReadImage(uri)); return slice; } } private static ImageStack AddSlice(ImageStack stack, OrthancConnection c, String uuid) throws IOException { ImageProcessor slice = DecodeInstance(c, uuid); if (stack == null) { stack = new ImageStack(slice.getWidth(), slice.getHeight()); } stack.addSlice("", slice); return stack; } static private class Slice implements Comparable { private Float index_; private String uuid_; Slice(float index, String uuid) { index_ = index; uuid_ = uuid; } @Override public int compareTo(Object other) { return index_.compareTo(((Slice) other).index_); } public String GetUuid() { return uuid_; } } private String[] SortSlices(List<Slice> slices) { Collections.sort(slices); String[] result = new String[slices.size()]; for (int i = 0; i < slices.size(); i++) { result[i] = slices.get(i).GetUuid(); } return result; } private String[] SortSlicesBy3D(OrthancConnection c, JSONArray instances) throws IOException { ArrayList<Slice> slices = new ArrayList<Slice>(); float normal[] = null; float minDistance = Float.POSITIVE_INFINITY; float maxDistance = Float.NEGATIVE_INFINITY; for (int i = 0; i < instances.size(); i++) { String uuid = (String) instances.get(i); JSONObject instance = (JSONObject) c.ReadJson("/instances/" + uuid + "/tags?simplify"); if (!instance.containsKey("ImageOrientationPatient") || !instance.containsKey("ImagePositionPatient")) { return null; } if (i == 0) { String[] tokens = ((String) instance.get("ImageOrientationPatient")).split("\\\\"); if (tokens.length != 6) { return null; } float cosines[] = new float[6]; for (int j = 0; j < 6; j++) { cosines[j] = Float.parseFloat(tokens[j]); } normal = new float[] { cosines[1] * cosines[5] - cosines[2] * cosines[4], cosines[2] * cosines[3] - cosines[0] * cosines[5], cosines[0] * cosines[4] - cosines[1] * cosines[3] }; } String[] tokens = ((String) instance.get("ImagePositionPatient")).split("\\\\"); if (tokens.length != 3) { return null; } float distance = 0; for (int j = 0; j < 3; j++) { distance += normal[j] * Float.parseFloat(tokens[j]); } minDistance = Math.min(minDistance, distance); maxDistance = Math.max(minDistance, distance); slices.add(new Slice(distance, uuid)); } if (maxDistance - minDistance < 0.001) { return null; } return SortSlices(slices); } private String[] SortSlicesByNumber(OrthancConnection c, JSONArray instances) throws IOException { ArrayList<Slice> slices = new ArrayList<Slice>(); for (int i = 0; i < instances.size(); i++) { String uuid = (String) instances.get(i); JSONObject instance = (JSONObject) c.ReadJson("/instances/" + uuid); Long index = (Long) instance.get("IndexInSeries"); slices.add(new Slice((float) index, uuid)); } return SortSlices(slices); } private String[] GetSlices(OrthancConnection c, JSONArray instances) throws IOException { String[] result; result = SortSlicesBy3D(c, instances); if (result != null && result.length == instances.size()) { return result; } result = SortSlicesByNumber(c, instances); if (result != null && result.length == instances.size()) { return result; } throw new IOException("Not a 3D image"); } private ImagePlus image_; public DicomDecoder(final OrthancConnection c, boolean isInstance, String uuid) throws IOException, InterruptedException, ExecutionException { ImageStack stack = null; JSONObject tags = null; String tagsUri, name; if (isInstance) { name = "Instance " + uuid; tags = (JSONObject) c.ReadJson("/instances/" + uuid + "/tags"); stack = AddSlice(stack, c, uuid); } else { name = "Series " + uuid; JSONObject series = (JSONObject) c.ReadJson("/series/" + uuid); JSONArray instances = (JSONArray) series.get("Instances"); try { tags = (JSONObject) c.ReadJson("/series/" + uuid + "/shared-tags"); } catch (Exception e) { // Fallback for old versions of Orthanc, without "shared-tags" tags = (JSONObject) c.ReadJson("/instances/" + (String) instances.get(0) + "/tags"); } final String[] slices = GetSlices(c, instances); final ProgressDialog progress = new ProgressDialog(slices.length); try { progress.setVisible(true); SwingWorker<ImageStack, Float> worker = new SwingWorker<ImageStack, Float>() { @Override protected ImageStack doInBackground() { try { ImageStack stack = null; for (int i = 0; i < slices.length; i++) { if (progress.IsCanceled()) { return null; } progress.SetProgress(i); stack = AddSlice(stack, c, slices[i]); } return stack; } catch (IOException e) { return null; } } }; worker.execute(); stack = worker.get(); } finally { progress.setVisible(false); } } image_ = new ImagePlus(name, stack); ExtractCalibration(image_, tags); ExtractPixelSpacing(image_, tags); ExtractDicomInfo(image_, tags); } public ImagePlus GetImage() { return image_; } }