OpenSlide with OMERO.web to render images not renderable by Bio-Formats?

This is a followup to my last post https://forum.image.sc/t/support-for-philips-tiff-and-conversion-to-ome-tiff/.
I’ve successfully added ome_seadragon as a plugin for OMERO.web. ome_seadragon uses OpenSlide for back-end image rendering. The plugin successfully generates the DZI deepzoom metadata for Philips TIFF i.e.

<Image xmlns=\"http://schemas.microsoft.com/deepzoom/2008\" Format=\"jpeg\" Overlap=\"1\" TileSize=\"256\"><Size Height=\"98304\" Width=\"208896\"/></Image>

but when getting the image tiles themselves via the GET request I receive a MissingPyramidException (full debug output below) (note ome_seadragon works with SVS). Table 1 of the paper Bringing Open Data to Whole Slide Imaging, explains that OpenSlide cover some WSI formats that the other API does not. So my question is: has anyone here successfully used OpenSlide with OMERO.web to render images not renderable by Bio-Formats?

Reference: http://www.crs4.it/results/technology-catalogue/ome-seadragon/
Github: https://github.com/crs4/ome_seadragon

Request Method: 	GET
Request URL: 	http://127.0.0.1:4080/ome_seadragon/deepzoom/get/301_files/9/0_0.jpeg
Django Version: 	1.8.19
Exception Type: 	MissingPyramidException
Exception Value: 	

exception ::omero::MissingPyramidException
{
    serverStackTrace = ome.conditions.MissingPyramidException: Missing pyramid:/home/omero/prog/omero/data/Pixels/301_pyramid
	at ome.io.nio.SimpleBackOff.throwMissingPyramidException(SimpleBackOff.java:94)
	at ome.io.nio.PixelsService.handleMissingPyramid(PixelsService.java:786)
	at ome.io.nio.PixelsService._getPixelBuffer(PixelsService.java:591)
	at ome.io.nio.PixelsService.getPixelBuffer(PixelsService.java:549)
	at ome.logic.RenderingSettingsImpl.resetDefaults(RenderingSettingsImpl.java:519)
	at ome.logic.RenderingSettingsImpl.resetDefaults(RenderingSettingsImpl.java:1429)
	at ome.services.RenderingBean$11.doWork(RenderingBean.java:2196)
	at sun.reflect.GeneratedMethodAccessor289.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at ome.services.util.Executor$Impl$Interceptor.invoke(Executor.java:568)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at ome.security.basic.EventHandler.invoke(EventHandler.java:154)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.orm.hibernate3.HibernateInterceptor.invoke(HibernateInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at ome.tools.hibernate.ProxyCleanupFilter$Interceptor.invoke(ProxyCleanupFilter.java:249)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at ome.services.util.ServiceHandler.invoke(ServiceHandler.java:121)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
	at com.sun.proxy.$Proxy80.doWork(Unknown Source)
	at ome.services.util.Executor$Impl.execute(Executor.java:447)
	at ome.services.util.Executor$Impl.execute(Executor.java:392)
	at ome.services.RenderingBean._resetDefaults(RenderingBean.java:2193)
	at ome.services.RenderingBean.internalReset(RenderingBean.java:713)
	at ome.services.RenderingBean.resetDefaultSettings(RenderingBean.java:677)
	at sun.reflect.GeneratedMethodAccessor1450.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at ome.services.util.ServiceHandler.invoke(ServiceHandler.java:121)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
	at com.sun.proxy.$Proxy112.resetDefaultSettings(Unknown Source)
	at sun.reflect.GeneratedMethodAccessor1450.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at ome.security.basic.BasicSecurityWiring.invoke(BasicSecurityWiring.java:93)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at ome.services.blitz.fire.AopContextInitializer.invoke(AopContextInitializer.java:43)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
	at com.sun.proxy.$Proxy112.resetDefaultSettings(Unknown Source)
	at sun.reflect.GeneratedMethodAccessor1452.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at ome.services.blitz.util.IceMethodInvoker.invoke(IceMethodInvoker.java:172)
	at ome.services.throttling.Callback.run(Callback.java:56)
	at ome.services.throttling.InThreadThrottlingStrategy.callInvokerOnRawArgs(InThreadThrottlingStrategy.java:56)
	at ome.services.blitz.impl.AbstractAmdServant.callInvokerOnRawArgs(AbstractAmdServant.java:140)
	at ome.services.blitz.impl.RenderingEngineI.resetDefaultSettings_async(RenderingEngineI.java:380)
	at sun.reflect.GeneratedMethodAccessor1451.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at omero.cmd.CallContext.invoke(CallContext.java:85)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
	at com.sun.proxy.$Proxy114.resetDefaultSettings_async(Unknown Source)
	at omero.api._RenderingEngineTie.resetDefaultSettings_async(_RenderingEngineTie.java:316)
	at omero.api._RenderingEngineDisp.___resetDefaultSettings(_RenderingEngineDisp.java:2014)
	at omero.api._RenderingEngineDisp.__dispatch(_RenderingEngineDisp.java:2434)
	at IceInternal.Incoming.invoke(Incoming.java:221)
	at Ice.ConnectionI.invokeAll(ConnectionI.java:2536)
	at Ice.ConnectionI.dispatch(ConnectionI.java:1145)
	at Ice.ConnectionI.message(ConnectionI.java:1056)
	at IceInternal.ThreadPool.run(ThreadPool.java:395)
	at IceInternal.ThreadPool.access$300(ThreadPool.java:12)
	at IceInternal.ThreadPool$EventHandlerThread.run(ThreadPool.java:832)
	at java.lang.Thread.run(Thread.java:748)

    serverExceptionClass = ome.conditions.MissingPyramidException
    message = Missing pyramid:/home/omero/prog/omero/data/Pixels/301_pyramid
    backOff = 54333849
    pixelsID = 301
}

Exception Location: 	/home/omero/prog/omero/OMERO.server/lib/python/omero_api_RenderingEngine_ice.py in resetDefaultSettings, line 2323
Python Executable: 	/usr/bin/python
Python Version: 	2.7.15
Python Path: 	

['/home/omero/prog/omero/OMERO.server-5.5.1-ice36-b122/lib/python/omeroweb',
 '/home/omero/prog/omero/OMERO.server-5.5.1-ice36-b122/lib/python/omeroweb',
 '/home/omero/prog/omero/webapps',
 '/home/omero/prog/omero/OMERO.server/lib/python',
 '/home/omero/prog/omero/OMERO.server/var/lib',
 '/home/omero/prog/omero/OMERO.server/lib/fallback',
 '/usr/lib/python2.7',
 '/usr/lib/python2.7/plat-x86_64-linux-gnu',
 '/usr/lib/python2.7/lib-tk',
 '/usr/lib/python2.7/lib-old',
 '/usr/lib/python2.7/lib-dynload',
 '/home/omero/.local/lib/python2.7/site-packages',
 '/usr/local/lib/python2.7/dist-packages',
 '/usr/lib/python2.7/dist-packages']

Server time: 	Fri, 25 Oct 2019 01:01:14 +0100

Apologies, I’m not familiar with ome_seadragon, but looking at the source code, it seems that it tries to get tiles from “openslide” (default settings) and then from “omero” https://github.com/crs4/ome_seadragon/blob/develop/views.py#L348
OpenSlide (and ome_seadragon) should work OK with WSI formats that Bio-Formats can’t read.

Your exception is coming from the failed attempt to get tiles from OMERO (fails because there is no Pyramid of tiles for Bio-Formats to read), but it may be masking the failure to first read tiles from OpenSlide, since the the Exception from getting the tile from the Primary rendering engine is only raised if the secondary rendering engine is None (Not raised if secondary rendering engine itself throws an exception).

But I can’t test this or help debug ome_seadragon much further I’m afraid.

Can you read tiles from the file using OpenSlide alone?

Will.

Hi @will-moore, I got ome_seadragon working properly now and yes OpenSlide does work. Those who are interested can try out the Docker Compose made by Luca with minor changes https://github.com/fireofearth/ome_seadragon_compose it also works with 3DHistech / Mirax images.

Note Mirax requires an importer https://github.com/crs4/ome_seadragon/blob/develop/tools/mirax_importer.py and not just omero import

Hi @fireofearth
Glad to hear it’s working and thanks for the links.
Will.

Could you provide a guide on how you installed ome_seadragon? I am trying to use it with the Moodle plugins and I am stuck on where I install it for omero_web app

Hi @Joao_Miguel , I set up ome_seadragon using this Docker compose: https://github.com/fireofearth/ome_seadragon_compose . As of now I’m having some trouble setting up ome_seadragon outside of the Docker compose since it’s only compatible with OMERO.server 5.4.1 and the installation process is pretty convoluted. I may release an updated revision of OME Seadragon to work with 5.5.1 / 5.6 once I get that working.

Sorry if it is a stupid question but what is a Docker? My institutional project depends on me accessing omero to use in moodle so I am extremely interested on installing ome_seadragon. I have installed omero 5.5.1 so I undestand I wont be able to follow your proposal to use ome_seadragon composer. Is that correct? Anything I can help to make it work?

Should I downgrade OMERO to 5.4.1 and try to install it? Could you please give me some guidance on the installation process? I really need to start working with Moodle and this plugin is essential for it

@Joao_Miguel sorry I took awhile to get back to you, Lucas just tried using OME Seadragon on OMERO.server 5.5 and says it works with that version (while I had some trouble with it initially).

see the docker images for testing:
https://github.com/lucalianas/ome_seadragon-docker/tree/ome_5.5
https://github.com/lucalianas/ome_seadragon-nginx-docker/tree/ome_5.5

Since you have are not using Docker you’ll have to install it as a Django plugin directly. You can try using the instructions: https://docs.openmicroscopy.org/omero/5.5.1/developers/Web/CreateApp.html

Unfortunately I can’t help you any more than that right now as I’m also figuring out how to use this outside of Docker.

@fireofearth Docker is a application “container” for running apps in a defined environment with all dependencies etc. Like a light-weight virtual machine. https://www.docker.com/why-docker

Docker compose allows you to set up several Docker containers to compose a complete app. E.g. https://github.com/lucalianas/ome_seadragon_compose/blob/develop/docker-compose.yml has an OMERO database, OMERO server, as well as several of seadragon web containers.
The setup of the ome_seadragon container is in https://github.com/crs4/ome_seadragon-docker/blob/master/Dockerfile, includes install of dependencies such as OpenSlide.

To run https://github.com/crs4/ome_seadragon outside of Docker, you need to have OMERO.web running somewhere. Then you can follow some of those instructions manually.

Essentially, you need to add the folder containing ome_seadragon to your $PYTHONPATH so it can be imported by the OMERO.web framework, and add ome_seadragon to the list of installed apps.

I just tried this (within the Python environment where you’re running OMERO.web)

$ git clone git@github.com:crs4/ome_seadragon.git
$ cd ome_seadragon/
$ pwd
/Users/will/Desktop/SEADRAGON/ome_seadragon
$ export PYTHONPATH=/Users/will/Desktop/SEADRAGON/:PYTHONPATH

Add to omero web apps - NB: note the single-double quotes.

$ omero config append omero.web.apps '"ome_seadragon"'

This was enough for me to restart OMERO.web and go to e.g.

localhost/ome_seadragon/get/projects/
I haven’t tried anything that requires OpenSlide yet…

Will.

1 Like

@Joao_Miguel here’s the updated Docker Compose for OME Seadragon using OMERO.server 5.5. And yes that’s essentially what Docker is and will-more explained it better than I could. For testing you only have to git clone the compose from here https://github.com/fireofearth/ome_seadragon_compose and do docker-compose up inside the repository using your terminal.

Will’s is correct about how to use OME Seadragon outside of Docker, however you also need to set up NGINX and OpenSlide for TIFF/MIRAX. Will get to that later…

Hi Will,
This is the third editing of this post but I thought I should do to facilitate to others:

  1. Cloning ome_seadragon in <your preferred location>:

git clone https://github.com/crs4/ome_seadragon.git

  1. Register the PYTHONPATH
    $ export PYTHONPATH=<your preferred location>:PYTHONPATH

  2. Add to omero web apps - NB: note the single-double quotes.

$ omero config append omero.web.apps ‘“ome_seadragon”’

  1. Check if it works
    http:/localhost/ome_seadragon/get/projects/

I struggled a bit with the cloning, PYTHONPATH as well as appending twice ome_seadragon, so I thought to put in a different format (your explanation was correct, but i struggled to understand it at first).
Many thanks