21
21
"""An example program to generate a Deep Zoom directory tree from a slide."""
22
22
23
23
from multiprocessing import Process , JoinableQueue
24
- from openslide import open_slide
24
+ from openslide import open_slide , ImageSlide
25
25
from openslide .deepzoom import DeepZoomGenerator
26
26
from optparse import OptionParser
27
27
import os
@@ -35,26 +35,39 @@ def __init__(self, queue, slidepath, tile_size, overlap):
35
35
self ._slidepath = slidepath
36
36
self ._tile_size = tile_size
37
37
self ._overlap = overlap
38
+ self ._slide = None
38
39
39
40
def run (self ):
40
- dz = DeepZoomGenerator (open_slide (self ._slidepath ), self ._tile_size ,
41
- self ._overlap )
41
+ self ._slide = open_slide (self ._slidepath )
42
+ last_associated = None
43
+ dz = self ._get_dz ()
42
44
while True :
43
45
data = self ._queue .get ()
44
46
if data is None :
45
47
self ._queue .task_done ()
46
48
break
47
- level , address , outfile = data
49
+ associated , level , address , outfile = data
50
+ if last_associated != associated :
51
+ dz = self ._get_dz (associated )
52
+ last_associated = associated
48
53
tile = dz .get_tile (level , address )
49
54
tile .save (outfile , optimize = True , quality = 90 )
50
55
self ._queue .task_done ()
51
56
57
+ def _get_dz (self , associated = None ):
58
+ if associated is not None :
59
+ image = ImageSlide (self ._slide .associated_images [associated ])
60
+ else :
61
+ image = self ._slide
62
+ return DeepZoomGenerator (image , self ._tile_size , self ._overlap )
63
+
52
64
53
65
class DeepZoomImageTiler (object ):
54
- def __init__ (self , dz , basename , format , queue ):
66
+ def __init__ (self , dz , basename , format , associated , queue ):
55
67
self ._dz = dz
56
68
self ._basename = basename
57
69
self ._format = format
70
+ self ._associated = associated
58
71
self ._queue = queue
59
72
self ._processed = 0
60
73
@@ -73,14 +86,16 @@ def _write_tiles(self):
73
86
tilename = os .path .join (tiledir , '%d_%d.%s' % (
74
87
col , row , self ._format ))
75
88
if not os .path .exists (tilename ):
76
- self ._queue .put ((level , (col , row ), tilename ))
89
+ self ._queue .put ((self ._associated , level , (col , row ),
90
+ tilename ))
77
91
self ._tile_done ()
78
92
79
93
def _tile_done (self ):
80
94
self ._processed += 1
81
95
count , total = self ._processed , self ._dz .tile_count
82
96
if count % 100 == 0 or count == total :
83
- print >> sys .stderr , "Wrote %d/%d tiles\r " % (count , total ),
97
+ print >> sys .stderr , "Tiling %s: wrote %d/%d tiles\r " % (
98
+ self ._associated or 'slide' , count , total ),
84
99
if count == total :
85
100
print
86
101
@@ -91,24 +106,40 @@ def _write_dzi(self):
91
106
92
107
class DeepZoomStaticTiler (object ):
93
108
def __init__ (self , slidepath , basename , format , tile_size , overlap ,
94
- workers ):
109
+ workers , with_viewer ):
95
110
self ._slide = open_slide (slidepath )
96
111
self ._basename = basename
97
112
self ._format = format
98
113
self ._tile_size = tile_size
99
114
self ._overlap = overlap
100
115
self ._queue = JoinableQueue (2 * workers )
101
116
self ._workers = workers
117
+ self ._with_viewer = with_viewer
102
118
for _i in range (workers ):
103
119
TileWorker (self ._queue , slidepath , tile_size , overlap ).start ()
104
120
105
121
def run (self ):
106
- dz = DeepZoomGenerator ( self ._slide , self . _tile_size , self . _overlap )
107
- tiler = DeepZoomImageTiler ( dz , self ._basename , self . _format ,
108
- self ._queue )
109
- tiler . run ( )
122
+ self ._run_image ( )
123
+ if self ._with_viewer :
124
+ for name in self ._slide . associated_images :
125
+ self . _run_image ( name )
110
126
self ._shutdown ()
111
127
128
+ def _run_image (self , associated = None ):
129
+ """Run a single image from self._slide."""
130
+ if associated is None :
131
+ image = self ._slide
132
+ if self ._with_viewer :
133
+ basename = os .path .join (self ._basename , 'slide' )
134
+ else :
135
+ basename = self ._basename
136
+ else :
137
+ image = ImageSlide (self ._slide .associated_images [associated ])
138
+ basename = os .path .join (self ._basename , associated )
139
+ dz = DeepZoomGenerator (image , self ._tile_size , self ._overlap )
140
+ DeepZoomImageTiler (dz , basename , self ._format , associated ,
141
+ self ._queue ).run ()
142
+
112
143
def _shutdown (self ):
113
144
for _i in range (self ._workers ):
114
145
self ._queue .put (None )
@@ -128,6 +159,9 @@ def _shutdown(self):
128
159
help = 'number of worker processes to start [4]' )
129
160
parser .add_option ('-o' , '--output' , metavar = 'NAME' , dest = 'basename' ,
130
161
help = 'base name of output file' )
162
+ parser .add_option ('-r' , '--viewer' , dest = 'with_viewer' ,
163
+ action = 'store_true' ,
164
+ help = 'generate directory tree with HTML viewer' )
131
165
parser .add_option ('-s' , '--size' , metavar = 'PIXELS' , dest = 'tile_size' ,
132
166
type = 'int' , default = 256 ,
133
167
help = 'tile size [256]' )
@@ -141,4 +175,5 @@ def _shutdown(self):
141
175
opts .basename = os .path .splitext (os .path .basename (slidepath ))[0 ]
142
176
143
177
DeepZoomStaticTiler (slidepath , opts .basename , opts .format ,
144
- opts .tile_size , opts .overlap , opts .workers ).run ()
178
+ opts .tile_size , opts .overlap , opts .workers ,
179
+ opts .with_viewer ).run ()
0 commit comments