problem with new FormatReader that calls native/JNI dll
Posted: Tue Oct 07, 2014 9:13 pm
I am trying to implement a new SlideBook reader which uses a JNI wrapper DLL to access a .SLD reader DLL called SBReadFile.dll . I have modeled my BioFormats reader class after LegacyND2Reader .
I've put my native .dlls in the lib\ folder along with LegacyND2Reader.dll, is there anything else I need to do so that they will get packaged and used properly?
My reader seems to successfully find/load the native JNI wrapper DLL (SBReadFileJNI.dll) using the static method:
But when the native closeFile method is called an UnsatisfiedLinkError exception is thrown.
I am stuck and could use some advice on how to debug what is wrong.
I am testing using the command: showinfo slide1.sld and the log output looks like:
Perhaps someone can spot a problem in the reader file (attached).
Thanks!
-- Richard
I've put my native .dlls in the lib\ folder along with LegacyND2Reader.dll, is there anything else I need to do so that they will get packaged and used properly?
My reader seems to successfully find/load the native JNI wrapper DLL (SBReadFileJNI.dll) using the static method:
- Code: Select all
static {
try {
System.loadLibrary("SBReadFileJNI");
}
catch (UnsatisfiedLinkError e) {
LOGGER.trace(NO_3I_MSG, e);
libraryFound = false;
}
catch (SecurityException e) {
LOGGER.warn("Insufficient permission to load native library", e);
}
}
But when the native closeFile method is called an UnsatisfiedLinkError exception is thrown.
- Code: Select all
Exception in thread "main" java.lang.UnsatisfiedLinkError: loci.formats.in.Slide
Book6Reader.closeFile()V
at loci.formats.in.SlideBook6Reader.closeFile(Native Method)
at loci.formats.in.SlideBook6Reader.close(SlideBook6Reader.java:232)
at loci.formats.ImageReader.close(ImageReader.java:536)
at loci.formats.ReaderWrapper.close(ReaderWrapper.java:339)
at loci.formats.ReaderWrapper.close(ReaderWrapper.java:339)
at loci.formats.FileStitcher.close(FileStitcher.java:519)
at loci.formats.in.FilePatternReader.close(FilePatternReader.java:241)
at loci.formats.ImageReader.close(ImageReader.java:536)
at loci.formats.ImageReader.close(ImageReader.java:757)
at loci.formats.ReaderWrapper.close(ReaderWrapper.java:573)
at loci.formats.tools.ImageInfo.configureReaderPreInit(ImageInfo.java:43
6)
at loci.formats.tools.ImageInfo.testRead(ImageInfo.java:988)
at loci.formats.tools.ImageInfo.main(ImageInfo.java:1074)
I am stuck and could use some advice on how to debug what is wrong.
I am testing using the command: showinfo slide1.sld and the log output looks like:
- Code: Select all
C:\Users\Richard\Documents\3i\git\tools>showinf ..\..\slide1.sld
Checking file format [3i SLD (native)]
Initializing reader
Perhaps someone can spot a problem in the reader file (attached).
Thanks!
-- Richard
- Code: Select all
package loci.formats.in;
import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Vector;
import loci.common.Constants;
import loci.common.DataTools;
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;
import ome.xml.model.primitives.PositiveFloat;
import loci.formats.MissingLibraryException;
/**
* SlideBook6Reader is a file format reader for 3i SlideBook SLD files that uses
* the SlideBook SBReadFile SDK - it is only usable on Windows machines.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/in/LegacyND2Reader.java">Trac</a>,
* <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/in/LegacyND2Reader.java;hb=HEAD">Gitweb</a></dd></dl>
*/
public class SlideBook6Reader extends FormatReader {
// -- Constants --
/** Modality types. */
private static final int WIDE_FIELD = 0;
private static final int BRIGHT_FIELD = 1;
private static final int LASER_SCAN_CONFOCAL = 2;
private static final int SPIN_DISK_CONFOCAL = 3;
private static final int SWEPT_FIELD_CONFOCAL = 4;
private static final int MULTI_PHOTON = 5;
private static final String URL_3I_SLD =
"http://www.openmicroscopy.org/site/support/bio-formats/formats/3i-slidebook6-sld.html";
private static final String NO_3I_MSG = "3i SlideBook SBReadFile library not found. " +
"Please see " + URL_3I_SLD + " for details.";
private static final String GENERAL_3I_MSG = "3i SlideBook SBReadFile library problem. " +
"Please see " + URL_3I_SLD + " for details.";
// -- Static initializers --
private static boolean libraryFound = true;
static {
try {
System.loadLibrary("SBReadFileJNI");
}
catch (UnsatisfiedLinkError e) {
LOGGER.trace(NO_3I_MSG, e);
libraryFound = false;
}
catch (SecurityException e) {
LOGGER.warn("Insufficient permission to load native library", e);
}
}
// -- Constructor --
public SlideBook6Reader() {
super("3i SLD (native)", new String[] {"sld"});
domains = new String[] {FormatTools.LM_DOMAIN};
}
// -- IFormatReader API methods --
/* @see IFormatReader#isThisType(String, boolean) */
public boolean isThisType(String file, boolean open) {
// Check the first few bytes of a file to determine if the file can be read by this reader.
// You can assume that index 0 in the stream corresponds to the index 0 in the file.
// Return true if the file can be read; false if not (or if there is no way of checking).
return libraryFound && super.isThisType(file, open);
}
/**
* @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int)
*/
public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h)
throws FormatException, IOException
{
// Returns a byte array containing the pixel data for a subimage specified image from the given file.
// The dimensions of the subimage (upper left X coordinate, upper left Y coordinate, width, and height) are
// specified in the final four int parameters. This should throw a FormatException if the image number is
// invalid (less than 0 or >= the number of images). The ordering of the array returned by openBytes should
// correspond to the values returned by isLittleEndian() and isInterleaved(). Also, the length of the byte array
// should be [image width * image height * bytes per pixel]. Extra bytes will generally be truncated. It is
// recommended that the first line of this method be:
// FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h) - this ensures that all of the parameters
// are valid.
FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h);
int[] zct = FormatTools.getZCTCoords(this, no);
int bpc = FormatTools.getBytesPerPixel(getPixelType());
byte[] b = new byte[FormatTools.getPlaneSize(this)];
readImagePlaneBuf(b, getSeries(), 0, zct[2], zct[0], zct[1]);
int pixel = bpc * getRGBChannelCount();
int rowLen = w * pixel;
for (int row=0; row<h; row++) {
System.arraycopy(b, pixel * ((row + y) * getSizeX() + x), buf,
row * rowLen, rowLen);
}
if (isRGB()) {
int bpp = getSizeC() * bpc;
int line = w * bpp;
for (int row=0; row<h; row++) {
for (int col=0; col<w; col++) {
int base = row * line + col * bpp;
for (int bb=0; bb<bpc; bb++) {
byte blue = buf[base + bpc*(getSizeC() - 1) + bb];
buf[base + bpc*(getSizeC() - 1) + bb] = buf[base + bb];
buf[base + bb] = blue;
}
}
}
}
return buf;
}
// -- Internal FormatReader API methods --
/* @see loci.formats.FormatReader#initFile(String) */
protected void initFile(String id) throws FormatException, IOException {
super.initFile(id);
// The majority of the file parsing logic should be placed in this method. The idea is to call this method
// once (and only once!) when the file is first opened. Generally, you will want to start by calling
// super.initFile(String). You will also need to set up the stream for reading the file, as well as initializing
// any dimension information and metadata. Most of this logic is up to you; however, you should populate the ‘core’
// variable (see loci.formats.CoreMetadata).
// Note that each variable is initialized to 0 or null when super.initFile(String) is called. Also,
// super.initFile(String) constructs a Hashtable called "metadata" where you should store any relevant metadata.
try {
openFile(id);
int numSeries = getNumCaptures();
core.clear();
for (int i=0; i<numSeries; i++) {
CoreMetadata ms = new CoreMetadata();
core.add(ms);
ms.sizeX = getNumXColumns(i);
if (ms.sizeX % 2 != 0) ms.sizeX++;
ms.sizeY = getNumYRows(i);
ms.sizeZ = getNumZPlanes(i);
ms.sizeT = getNumTimepoints(i);
ms.sizeC = getNumChannels(i);
int bytes = getBytesPerPixel(i);
if (bytes % 3 == 0) {
ms.sizeC *= 3;
bytes /= 3;
ms.rgb = true;
}
else ms.rgb = false;
ms.pixelType = FormatTools.pixelTypeFromBytes(bytes, false, true);
ms.imageCount = ms.sizeZ * ms.sizeT;
if (!ms.rgb) ms.imageCount *= ms.sizeC;
ms.interleaved = true;
ms.littleEndian = true;
ms.dimensionOrder = "XYCZT";
ms.indexed = false;
ms.falseColor = false;
}
}
catch (UnsatisfiedLinkError e) {
throw new MissingLibraryException(GENERAL_3I_MSG, e);
}
catch (Exception e) {
throw new MissingLibraryException(GENERAL_3I_MSG, e);
}
MetadataStore store = makeFilterMetadata();
MetadataTools.populatePixels(store, this);
for (int i=0; i<getNumCaptures(); i++) {
store.setImageName("Image " + (i + 1), i);
}
}
public void close(boolean fileOnly) {
// Cleans up any resources used by the reader. Global variables should be reset to their initial state, and any
// open files or delegate readers should be closed.
closeFile();
}
// -- Native methods --
public native boolean openFile(String path);
public native void closeFile();
public native int getNumCaptures();
public native int getNumPositions(int inCapture);
public native int getNumTimepoints(int inCapture);
public native int getNumChannels(int inCapture);
public native int getNumXColumns(int inCapture);
public native int getNumYRows(int inCapture);
public native int getNumZPlanes(int inCapture);
public native int getElapsedTime(int inCapture, int inTimepoint);
public native int getExposureTime(int inCapture, int inChannel);
public native float getVoxelSize(int inCapture);
public native double getXPosition(int inCapture, int inPosition);
public native double getYPosition(int inCapture, int inPosition);
public native double getZPosition(int inCapture, int inPosition, int inZPlane);
public native int getMontageRow(int inCapture, int inPosition);
public native int getMontageColumn(int inCapture, int inPosition);
public native String getChannelName(int inCapture, int inChannel);
public native String getLensName(int inCapture);
public native double getMagnification(int inCapture);
public native String getImageName(int inCapture);
public native String getImageComments(int inCapture);
public native int getBytesPerPixel(int inCapture);
public native boolean readImagePlaneBuf( byte outPlaneBuffer[],
int inCapture,
int inPosition,
int inTimepoint,
int inZ,
int inChannel );
}