/*
* #%L
* OME Bio-Formats package for reading and converting biological file formats.
* %%
* Copyright (C) 2005 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* 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 2 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/gpl-2.0.html>.
* #L%
*/
package loci.formats.in;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import ome.xml.model.primitives.Timestamp;
import loci.common.DataTools;
import loci.common.DateTools;
import loci.common.Location;
import loci.common.RandomAccessInputStream;
import loci.formats.CoreMetadata;
import loci.formats.FormatException;
import loci.formats.FormatReader;
import loci.formats.FormatTools;
import loci.formats.MetadataTools;
import loci.formats.meta.MetadataStore;
/**
* BrukerReader is the file format reader for Bruker MRI files.
*
* @author Melissa Linkert melissa at glencoesoftware.com
*/
public class BrukerReader extends FormatReader {
// -- Constants --
private static final String DATE_FORMAT = "HH:mm:ss d MMM yyyy";
// -- Fields --
private ArrayList<String> pixelsFiles = new ArrayList<String>();
private ArrayList<String> allFiles = new ArrayList<String>();
private int lastSeries = -1;
private RandomAccessInputStream seriesStream;
// -- Constructor --
/** Constructs a new Bruker reader. */
public BrukerReader() {
super("Bruker", "");
suffixSufficient = false;
domains = new String[] {FormatTools.MEDICAL_DOMAIN};
hasCompanionFiles = true;
datasetDescription = "One 'fid' and one 'acqp' plus several other " +
"metadata files and a 'pdata' directory";
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#getRequiredDirectories(String[]) */
@Override
public int getRequiredDirectories(String[] files)
throws FormatException, IOException
{
return 1;
}
/* @see loci.formats.IFormatReader#isSingleFile(String) */
@Override
public boolean isSingleFile(String id) throws FormatException, IOException {
return false;
}
/* @see loci.formats.IFormatReader#isThisType(String, boolean) */
@Override
public boolean isThisType(String name, boolean open) {
Location file = new Location(name).getAbsoluteFile();
return file.getName().equals("fid") || file.getName().equals("acqp");
}
/* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */
@Override
public boolean isThisType(RandomAccessInputStream stream) throws IOException {
return false;
}
/**
* @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int)
*/
@Override
public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h)
throws FormatException, IOException
{
FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h);
if (getSeries() != lastSeries) {
if (seriesStream != null) {
seriesStream.close();
}
seriesStream = new RandomAccessInputStream(pixelsFiles.get(getSeries()));
lastSeries = getSeries();
}
seriesStream.seek((long) no * FormatTools.getPlaneSize(this));
readPlane(seriesStream, x, y, w, h, buf);
return buf;
}
/* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */
@Override
public String[] getSeriesUsedFiles(boolean noPixels) {
FormatTools.assertId(currentId, true, 1);
String dir = pixelsFiles.get(getSeries());
Location realDir = new Location(dir).getParentFile();
realDir = realDir.getParentFile();
realDir = realDir.getParentFile();
dir = realDir.getAbsolutePath();
ArrayList<String> files = new ArrayList<String>();
files.add(new Location(getCurrentFile()).getAbsolutePath());
for (String f : allFiles) {
if (f.startsWith(dir) && (!f.endsWith("2dseq") || !noPixels)) {
if (!files.contains(f)) {
files.add(f);
}
}
}
return files.toArray(new String[files.size()]);
}
/* @see loci.formats.IFormatReader#fileGroupOption(String) */
@Override
public int fileGroupOption(String id) throws FormatException, IOException {
return FormatTools.MUST_GROUP;
}
/* @see loci.formats.IFormatReader#close(boolean) */
@Override
public void close(boolean fileOnly) throws IOException {
super.close(fileOnly);
if (!fileOnly) {
pixelsFiles.clear();
allFiles.clear();
lastSeries = -1;
if (seriesStream != null) {
seriesStream.close();
}
seriesStream = null;
}
}
// -- Internal FormatReader API methods --
/* @see loci.formats.FormatReader#initFile(String) */
@Override
protected void initFile(String id) throws FormatException, IOException {
super.initFile(id);
Location originalFile = new Location(id).getAbsoluteFile();
Location parent = originalFile.getParentFile().getParentFile();
String[] acquisitionDirs = parent.list(true);
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
Integer i1 = 0;
try {
i1 = new Integer(s1);
}
catch (NumberFormatException e) { }
Integer i2 = 0;
try {
i2 = new Integer(s2);
}
catch (NumberFormatException e) { }
return i1.compareTo(i2);
}
};
Arrays.sort(acquisitionDirs, comparator);
ArrayList<String> acqpFiles = new ArrayList<String>();
ArrayList<String> recoFiles = new ArrayList<String>();
for (String f : acquisitionDirs) {
Location dir = new Location(parent, f);
if (dir.isDirectory()) {
String[] files = dir.list(true);
for (String file : files) {
Location child = new Location(dir, file);
if (!child.isDirectory()) {
allFiles.add(child.getAbsolutePath());
if (file.equals("acqp")) {
acqpFiles.add(child.getAbsolutePath());
}
}
else {
Location grandchild = new Location(child, "1");
if (grandchild.exists()) {
String[] moreFiles = grandchild.list(true);
for (String m : moreFiles) {
Location ggc = new Location(grandchild, m);
if (!ggc.isDirectory()) {
allFiles.add(ggc.getAbsolutePath());
if (m.equals("2dseq")) {
pixelsFiles.add(ggc.getAbsolutePath());
}
else if (m.equals("reco")) {
recoFiles.add(ggc.getAbsolutePath());
}
}
}
}
}
}
if (acqpFiles.size() > pixelsFiles.size()) {
acqpFiles.remove(acqpFiles.size() - 1);
}
if (recoFiles.size() > pixelsFiles.size()) {
recoFiles.remove(recoFiles.size() - 1);
}
}
}
String[] imageNames = new String[pixelsFiles.size()];
String[] timestamps = new String[pixelsFiles.size()];
String[] institutions = new String[pixelsFiles.size()];
String[] users = new String[pixelsFiles.size()];
core.clear();
for (int series=0; series<pixelsFiles.size(); series++) {
CoreMetadata ms = new CoreMetadata();
core.add(ms);
setSeries(series);
String acqData = DataTools.readFile(acqpFiles.get(series));
String[] lines = acqData.split("\n");
String[] sizes = null;
String[] ordering = null;
int ni = 0, nr = 0, ns = 0;
int bits = 0;
boolean signed = false;
boolean isFloat = false;
for (int i=0; i<lines.length; i++) {
String line = lines[i];
int index = line.indexOf("=");
if (index >= 0) {
String key = line.substring(0, index);
String value = line.substring(index + 1);
if (value.startsWith("(")) {
value = lines[i + 1].trim();
if (value.startsWith("<")) {
value = value.substring(1, value.length() - 1);
}
}
if (key.length() < 4) {
continue;
}
addSeriesMeta(key.substring(3), value);
if (key.equals("##$NI")) {
ni = Integer.parseInt(value);
}
else if (key.equals("##$NR")) {
nr = Integer.parseInt(value);
}
else if (key.equals("##$ACQ_word_size")) {
bits = Integer.parseInt(value.substring(1, value.lastIndexOf("_")));
}
else if (key.equals("##$BYTORDA")) {
ms.littleEndian = value.toLowerCase().equals("little");
}
else if (key.equals("##$ACQ_size")) {
sizes = value.split(" ");
}
else if (key.equals("##$ACQ_obj_order")) {
ordering = value.split(" ");
}
else if (key.equals("##$ACQ_time")) {
timestamps[series] = value;
}
else if (key.equals("##$ACQ_institution")) {
institutions[series] = value;
}
else if (key.equals("##$ACQ_operator")) {
users[series] = value;
}
else if (key.equals("##$ACQ_scan_name")) {
imageNames[series] = value;
}
else if (key.equals("##$ACQ_ns_list_size")) {
ns = Integer.parseInt(value);
}
}
}
String recoData = DataTools.readFile(recoFiles.get(series));
lines = recoData.split("\n");
for (int i=0; i<lines.length; i++) {
String line = lines[i];
int index = line.indexOf("=");
if (index >= 0) {
String key = line.substring(0, index);
String value = line.substring(index + 1);
if (value.startsWith("(")) {
value = lines[i + 1].trim();
if (value.startsWith("<")) {
value = value.substring(1, value.length() - 1);
}
}
if (key.length() < 4) {
continue;
}
addSeriesMeta(key.substring(3), value);
if (key.equals("##$RECO_size")) {
sizes = value.split(" ");
}
else if (key.equals("##$RECO_wordtype")) {
bits = Integer.parseInt(value.substring(1, value.indexOf("BIT")));
signed = value.indexOf("_SGN_") >= 0;
isFloat = !value.endsWith("_INT");
}
}
}
int td = Integer.parseInt(sizes[0]);
int ys = sizes.length > 1 ? Integer.parseInt(sizes[1]) : 0;
int zs = sizes.length > 2 ? Integer.parseInt(sizes[2]) : 0;
if (sizes.length == 2) {
if (ni == 1) {
ms.sizeY = ys;
ms.sizeZ = nr;
}
else {
ms.sizeY = ys;
ms.sizeZ = ni;
}
}
else if (sizes.length == 3) {
ms.sizeY = ni * ys;
ms.sizeZ = nr * zs;
}
ms.sizeX = td;
ms.sizeZ /= ns;
ms.sizeT = ns * nr;
ms.sizeC = 1;
ms.imageCount = getSizeZ() * getSizeC() * getSizeT();
ms.dimensionOrder = "XYCTZ";
ms.rgb = false;
ms.interleaved = false;
ms.pixelType =
FormatTools.pixelTypeFromBytes(bits / 8, signed, isFloat);
}
MetadataStore store = makeFilterMetadata();
MetadataTools.populatePixels(store, this);
for (int series=0; series<getSeriesCount(); series++) {
store.setImageName(imageNames[series] + " #" + (series + 1), series);
String date = DateTools.formatDate(timestamps[series], DATE_FORMAT);
if (date != null) {
store.setImageAcquisitionDate(new Timestamp(date), series);
}
String expID = MetadataTools.createLSID("Experimenter", series);
store.setExperimenterID(expID, series);
store.setExperimenterLastName(users[series], series);
store.setExperimenterInstitution(institutions[series], series);
store.setImageExperimenterRef(expID, series);
}
}
}