Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RGB support #215

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 136 additions & 14 deletions src/main/java/com/glencoesoftware/bioformats2raw/Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ public class Converter implements Callable<Integer> {
private IProgressListener progressListener;
private Map<Integer, int[]> tileCounts = new HashMap<Integer, int[]>();

/** Whether or not to preserve channel storage for RGB data. */
private boolean keepRGB = false;

// Option setters

/**
Expand Down Expand Up @@ -838,6 +841,21 @@ public void setDimensionOrder(DimensionOrder order) {
dimensionOrder = order;
}

/**
* Set whether or not to preserve channel ordering for input RGB data.
* False by default, so channels will be separated.
*
* @param keep true if RGB storage should be preserved
*/
@Option(
names = "--keep-rgb",
description = "Keep RGB data (if present) as RGB",
defaultValue = "false"
)
public void setKeepRGB(boolean keep) {
keepRGB = keep;
}

// Option getters

/**
Expand Down Expand Up @@ -1075,12 +1093,19 @@ public int getMinImageSize() {
}

/**
* @return current dimension roder
* @return current dimension order
*/
public DimensionOrder getDimensionOrder() {
return dimensionOrder;
}

/**
* @return whether or not to preserve RGB data
*/
public boolean getKeepRGB() {
return keepRGB;
}

// Conversion methods

/**
Expand Down Expand Up @@ -1237,17 +1262,20 @@ public void convert()
memoizer.setFlattenedResolutions(false);
memoizer.setMetadataFiltered(true);
memoizer.setMetadataStore(createMetadata());
ChannelSeparator separator = new ChannelSeparator(memoizer);
separator.setId(inputPath.toString());
separator.setResolution(0);
IFormatReader wrappedReader = memoizer;
if (!getKeepRGB()) {
wrappedReader = new ChannelSeparator(wrappedReader);
}
wrappedReader.setId(inputPath.toString());
wrappedReader.setResolution(0);
if (reader instanceof MiraxReader) {
((MiraxReader) reader).setTileCache(tileCache);
}
if (omeroMetadata) {
readers.add(new MinMaxCalculator(separator));
readers.add(new MinMaxCalculator(wrappedReader));
}
else {
readers.add(separator);
readers.add(wrappedReader);
}
savedMemoFile = savedMemoFile || memoizer.isSavedToMemo();
}
Expand Down Expand Up @@ -1777,21 +1805,41 @@ private byte[] getTileDownsampled(
String.format(scaleFormatString,
getScaleFormatStringArgs(series, resolution - 1));
final ZarrArray zarr = ZarrArray.open(getRootPath().resolve(pathName));

int[] dimensions = zarr.getShape();
int[] blockSizes = zarr.getChunks();
int activeTileWidth = blockSizes[blockSizes.length - 1];
int activeTileHeight = blockSizes[blockSizes.length - 2];

boolean interleaved = false;
IFormatReader r = readers.take();
try {
interleaved = r.isInterleaved();
}
finally {
readers.put(r);
}

int xIndex = blockSizes.length - 1;
int yIndex = blockSizes.length - 2;
int channels = 1;
if (getKeepRGB() && interleaved) {
xIndex--;
yIndex--;
channels = blockSizes[blockSizes.length - 1];
}

int activeTileWidth = blockSizes[xIndex];
int activeTileHeight = blockSizes[yIndex];

// Upscale our base X and Y offsets, and sizes to the previous resolution
// based on the pyramid scaling factor
xx *= PYRAMID_SCALE;
yy *= PYRAMID_SCALE;
width = (int) Math.min(
activeTileWidth * PYRAMID_SCALE,
dimensions[dimensions.length - 1] - xx);
dimensions[xIndex] - xx);
height = (int) Math.min(
activeTileHeight * PYRAMID_SCALE,
dimensions[dimensions.length - 2] - yy);
dimensions[yIndex] - yy);

IFormatReader reader = readers.take();
int[] offset;
Expand All @@ -1804,13 +1852,18 @@ private byte[] getTileDownsampled(

int bytesPerPixel = FormatTools.getBytesPerPixel(pixelType);
int[] shape = new int[] {1, 1, 1, height, width};
if (getKeepRGB() && interleaved) {
shape[2] = height;
shape[3] = width;
shape[4] = channels;
}
byte[] tileAsBytes = readAsBytes(zarr, shape, offset);

if (downsampling == Downsampling.SIMPLE) {
return scaler.downsample(tileAsBytes, width, height,
PYRAMID_SCALE, bytesPerPixel, false,
FormatTools.isFloatingPoint(pixelType),
1, false);
channels, interleaved);
}

return OpenCVTools.downsample(
Expand Down Expand Up @@ -1878,6 +1931,19 @@ private int[] getDimensions(
dimensions[o.indexOf("Z")] = sizeZ;
dimensions[o.indexOf("C")] = sizeC;
dimensions[o.indexOf("T")] = sizeT;

if (getKeepRGB() && reader.getRGBChannelCount() > 1) {
if (reader.isInterleaved()) {
dimensions[1] = scaledDepth;
dimensions[2] = scaledHeight;
dimensions[3] = scaledWidth;
dimensions[4] = reader.getRGBChannelCount();
}
else {
dimensions[1] = scaledDepth;
dimensions[2] = reader.getRGBChannelCount();
}
}
return dimensions;
}

Expand All @@ -1902,6 +1968,14 @@ private int[] getOffset(
offset[o.indexOf("Z")] = zct[0];
offset[o.indexOf("C")] = zct[1];
offset[o.indexOf("T")] = zct[2];

if (getKeepRGB() && reader.getRGBChannelCount() > 1) {
if (reader.isInterleaved()) {
offset[2] = y;
offset[3] = x;
offset[4] = 0;
}
}
return offset;
}

Expand Down Expand Up @@ -2000,21 +2074,45 @@ public void saveResolutions(int series)
int sizeC;
int imageCount;
String readerDimensionOrder;
int rgbChannels;
boolean interleaved;
try {
// calculate a reasonable pyramid depth if not specified as an argument
sizeX = workingReader.getSizeX();
sizeY = workingReader.getSizeY();
sizeZ = workingReader.getSizeZ();
sizeT = workingReader.getSizeT();
sizeC = workingReader.getSizeC();
sizeC = workingReader.getEffectiveSizeC();
readerDimensionOrder = workingReader.getDimensionOrder();
imageCount = workingReader.getImageCount();
pixelType = workingReader.getPixelType();

rgbChannels = workingReader.getRGBChannelCount();
interleaved = workingReader.isInterleaved();

if (rgbChannels != workingReader.getSizeC() && getKeepRGB()) {
throw new UnsupportedOperationException(
"Mixed channel types not supported with '--keep-rgb'");
}
}
finally {
readers.put(workingReader);
}

boolean opencv = getDownsampling() != Downsampling.SIMPLE;
if ((pixelType == FormatTools.INT8 || pixelType == FormatTools.INT32) &&
opencv && resolutions > 0)
{
String type = FormatTools.getPixelTypeString(pixelType);
throw new UnsupportedOperationException(
"OpenCV does not support downsampling " + type + " data. " +
"See https://github.com/opencv/opencv/issues/7862");
}
else if (interleaved && rgbChannels > 1 && opencv) {
throw new UnsupportedOperationException(
"Downsampling RGB data with OpenCV not supported");
}

LOGGER.info(
"Preparing to write pyramid sizeX {} (tileWidth: {}) " +
"sizeY {} (tileWidth: {}) sizeZ {} (tileDepth: {}) imageCount {}",
Expand Down Expand Up @@ -2066,14 +2164,27 @@ public void saveResolutions(int series)
activeChunkDepth = scaledDepth;
}

int[] chunkSize = new int[] {
1, 1, activeChunkDepth, activeTileHeight, activeTileWidth};
if (getKeepRGB() && rgbChannels > 1) {
chunkSize[1] = activeChunkDepth;
if (interleaved) {
chunkSize[2] = activeTileHeight;
chunkSize[3] = activeTileWidth;
chunkSize[4] = rgbChannels;
}
else {
chunkSize[2] = rgbChannels;
}
}

DataType dataType = getZarrType(pixelType);
String resolutionString = String.format(
scaleFormatString, getScaleFormatStringArgs(series, resolution));
ArrayParams arrayParams = new ArrayParams()
.shape(getDimensions(
workingReader, scaledWidth, scaledHeight, scaledDepth))
.chunks(new int[] {1, 1, activeChunkDepth, activeTileHeight,
activeTileWidth})
.chunks(chunkSize)
.dataType(dataType)
.dimensionSeparator(getDimensionSeparator())
.compressor(CompressorFactory.create(
Expand Down Expand Up @@ -2109,6 +2220,14 @@ public void saveResolutions(int series)
executor.execute(() -> {
try {
int[] shape = {1, 1, 1, height, width};
if (getKeepRGB() && rgbChannels > 1) {
if (interleaved) {
shape = new int[] {1, 1, height, width, rgbChannels};
}
else {
shape = new int[] {1, 1, rgbChannels, height, width};
}
}
int[] offset;
IFormatReader reader = readers.take();
try {
Expand Down Expand Up @@ -2376,6 +2495,9 @@ private void setSeriesLevelMetadata(int series, int resolutions)
else {
axisOrder = v.getDimensionOrder();
}
if (getKeepRGB() && v.isInterleaved()) {
axisOrder = "C" + axisOrder.replaceAll("[Cc]", "");
}
}
finally {
readers.put(v);
Expand Down
Loading