1 | """ |
---|
2 | |
---|
3 | This script is a "proof of principle" to demonstrate how Spider and EMAN2 can work with OMERO. |
---|
4 | This uses the auto-box functionality of EMAN2, which takes one or more user-defined particle ROIs, |
---|
5 | and uses this as the basis for picking additional particles from the image. |
---|
6 | In this script, OMERO is used as the source of the image, with the user-defined particles as ROIs on |
---|
7 | the server. Spider is used to turn the plane array into a Spider image. The EMAN2 subclasses |
---|
8 | use this, with the server ROI(s) to generate additional ROIs which are then saved back to |
---|
9 | the server. |
---|
10 | The script below is currently implemented as a client-side script, but could easily be |
---|
11 | converted into a server-side script, with EMAN2 and Spider installed on the server. |
---|
12 | |
---|
13 | """ |
---|
14 | from EMAN2 import * |
---|
15 | |
---|
16 | from e2boxer import * |
---|
17 | |
---|
18 | import numpy |
---|
19 | import omero |
---|
20 | from omero.rtypes import * |
---|
21 | import omero.util.script_utils as scriptUtil |
---|
22 | from Spider.Spiderarray import array2spider |
---|
23 | |
---|
24 | from Tkinter import Tk, Label |
---|
25 | import Image, ImageTk |
---|
26 | |
---|
27 | |
---|
28 | class DummyWindow(): |
---|
29 | """ |
---|
30 | Just need a blank class to replace the need for a UI |
---|
31 | """ |
---|
32 | def updateGL(self): |
---|
33 | pass |
---|
34 | |
---|
35 | |
---|
36 | class OmeroSwarmPanel(): |
---|
37 | """ |
---|
38 | Another blank class to replace the need for a UI |
---|
39 | """ |
---|
40 | |
---|
41 | def __init__(self, box_size): |
---|
42 | self.box_size = box_size |
---|
43 | |
---|
44 | def set_picking_data(self, peak_score, profile, profile_trough_point): |
---|
45 | print "Dummy PanelObject: set_picking_data()" |
---|
46 | print " peak_score: " + str(peak_score) |
---|
47 | print " profile: " + str(profile) |
---|
48 | print " profile_trough_point: " + str(profile_trough_point) |
---|
49 | |
---|
50 | |
---|
51 | class OmeroSwarmTool(SwarmBoxer): |
---|
52 | ''' |
---|
53 | Subclass the main Boxer class, to add data source, UI elements etc. |
---|
54 | ''' |
---|
55 | |
---|
56 | def __init__(self,target,particle_diameter=128): |
---|
57 | # import sys |
---|
58 | # print >> sys.stderr, target.__class__ |
---|
59 | SwarmBoxer.__init__(self,particle_diameter) |
---|
60 | self.target = weakref.ref(target) # now, the target() method will return target |
---|
61 | window = DummyWindow() |
---|
62 | def getWindow(): |
---|
63 | return window |
---|
64 | self.get_2d_window = getWindow |
---|
65 | #self.panel_object = SwarmPanel(self,self.particle_diameter) |
---|
66 | self.panel_object = OmeroSwarmPanel(self.particle_diameter) # needs to implement set_picking_data(self.peak_score, self.profile, self.profile_trough_point) |
---|
67 | self.gui_mode = False |
---|
68 | |
---|
69 | |
---|
70 | class Target(): |
---|
71 | """ |
---|
72 | Dummy target to get / save boxes to omero etc. |
---|
73 | Replaces the functionality of emboxerbase.EMBoxerModule which is used in the workflow UI. |
---|
74 | Takes a reference to an OMERO session in the constructor, which is then used to write the picked particles as |
---|
75 | ROIs to the image, identified by imageId |
---|
76 | """ |
---|
77 | def __init__(self, box_size, session, imageId): |
---|
78 | def getFileName(): |
---|
79 | return "/Users/will/Documents/dev/EMAN2/06jul12a.mrc" |
---|
80 | self.current_file = getFileName |
---|
81 | #self.current_file = "/Users/will/Documents/dev/EMAN2/06jul12a.mrc" |
---|
82 | self.box_size = box_size |
---|
83 | self.box_list = EMBoxList(self) # has methods like detect_collision() |
---|
84 | self.session = session |
---|
85 | self.imageId = imageId |
---|
86 | |
---|
87 | # create the service and image for adding ROIs later... |
---|
88 | self.updateService = self.session.getUpdateService() |
---|
89 | gateway = self.session.createGateway() |
---|
90 | self.image = gateway.getImage(self.imageId) |
---|
91 | self.imageY = self.image.getPrimaryPixels().getSizeY().getValue() |
---|
92 | |
---|
93 | # code from emboxerbase.EMBoxerModule |
---|
94 | def add_box(self, x, y, type): |
---|
95 | """ |
---|
96 | add a box to the list |
---|
97 | If type = SwarmBoxer.REF_NAME then this is a reference box. |
---|
98 | """ |
---|
99 | print "add_box() x: %d, y: %d" % (x, y) |
---|
100 | box_num = self.box_list.add_box(x,y,type=type) |
---|
101 | |
---|
102 | |
---|
103 | # code from emboxerbase.EMBoxerModule |
---|
104 | def clear_boxes(self,type,cache=False): |
---|
105 | self.box_list.clear_boxes(type,cache=cache) |
---|
106 | |
---|
107 | |
---|
108 | # code from emboxerbase.EMBoxerModule |
---|
109 | def get_box(self,box_number): |
---|
110 | ''' |
---|
111 | @param box_number the number of the box for which you want to get |
---|
112 | ''' |
---|
113 | return self.box_list.get_box(box_number) |
---|
114 | |
---|
115 | |
---|
116 | # code from emboxerbase.EMBoxerModule |
---|
117 | def set_box(self,box,box_number,update_display=False): |
---|
118 | ''' |
---|
119 | @param box_number the number of the box for which you want to get |
---|
120 | ''' |
---|
121 | self.box_list.set_box(box,box_number) |
---|
122 | #if update_display: |
---|
123 | # self.full_box_update() |
---|
124 | |
---|
125 | |
---|
126 | # code from emboxerbase.EMBoxerModule |
---|
127 | def add_boxes(self,boxes,update_gl=True): |
---|
128 | ''' |
---|
129 | boxes should be a list like [[x,y,type],[x,y,type],....[int,int,string]] |
---|
130 | ''' |
---|
131 | for b in boxes: |
---|
132 | print b |
---|
133 | x,y,type,v = b |
---|
134 | x -= self.box_size/2 # convert from centre of particle, to top-left of ROI |
---|
135 | y = self.imageY - y # convert from bottom to top Y coordinates. |
---|
136 | y -= self.box_size/2 |
---|
137 | self.addRectangleRoi(x, y) |
---|
138 | # removed a lot of UI code from emboxerbase.EMBoxerModule |
---|
139 | self.box_list.add_boxes(boxes) |
---|
140 | |
---|
141 | |
---|
142 | # code from emboxerbase.EMBoxerModule |
---|
143 | def get_subsample_rate(self): |
---|
144 | ''' |
---|
145 | |
---|
146 | ''' |
---|
147 | return int(math.ceil(float(self.box_size)/float(TEMPLATE_MIN))) |
---|
148 | |
---|
149 | # code from emboxerbase.EMBoxerModule |
---|
150 | def get_exclusion_image(self,mark_boxes=False): |
---|
151 | ''' |
---|
152 | @mark_boxes if true the exclusion image is copied and the locations of the current boxes are painted in as excluded regions |
---|
153 | This is useful for autoboxers - they obviously dont want to box any region that already has a box in it (such as a manual box, |
---|
154 | or a previously autoboxed box) |
---|
155 | ''' |
---|
156 | exc_image = ScaledExclusionImageCache.get_image(self.current_file(), self.get_subsample_rate()) # class 'libpyEMData2.EMData' |
---|
157 | print "Target get_exclusion_image() mark_boxes: " + str(mark_boxes) |
---|
158 | |
---|
159 | #display(exc_image) # blank image |
---|
160 | |
---|
161 | if not mark_boxes: return exc_image |
---|
162 | |
---|
163 | else: |
---|
164 | print " Ignoring request to mark_boxes: returning unmasked image..." |
---|
165 | return exc_image # hack to avoid fixing the code below. Manually added boxes will not be excluded from being auto-boxed. |
---|
166 | |
---|
167 | image = exc_image.copy() |
---|
168 | boxes = self.box_list.get_boxes() |
---|
169 | if len(boxes) > 0: |
---|
170 | sr = self.get_subsample_rate() |
---|
171 | global BinaryCircleImageCache |
---|
172 | mask = BinaryCircleImageCache.get_image_directly(int(self.box_size/(2*sr))) |
---|
173 | for box in self.box_list.get_boxes(): |
---|
174 | x,y = int(box.x/sr),int(box.y/sr) |
---|
175 | # from EMAN2 import BoxingTools |
---|
176 | BoxingTools.set_region(image,mask,x,y,0.1) # 0.1 is also the value set by the eraser - all that matters is that it's zon_zero |
---|
177 | |
---|
178 | return image |
---|
179 | |
---|
180 | # code from emboxerbase.EMBoxerModule |
---|
181 | def detect_box_collision(self,data): |
---|
182 | print "target detect_box_collision() " + str(data) |
---|
183 | return self.box_list.detect_collision(data[0], data[1], self.box_size) |
---|
184 | |
---|
185 | |
---|
186 | def addRectangleRoi(self, x, y): |
---|
187 | """ |
---|
188 | Adds a Rectangle (particle) to the current OMERO image, at point x, y. |
---|
189 | Uses the self.image (OMERO image) and self.updateService |
---|
190 | """ |
---|
191 | width = self.box_size |
---|
192 | height = self.box_size |
---|
193 | |
---|
194 | # create an ROI, add the rectangle and save |
---|
195 | roi = omero.model.RoiI() |
---|
196 | roi.setImage(self.image) |
---|
197 | r = self.updateService.saveAndReturnObject(roi) |
---|
198 | |
---|
199 | # create and save a rectangle shape |
---|
200 | rect = omero.model.RectI() |
---|
201 | rect.x = rdouble(x) |
---|
202 | rect.y = rdouble(y) |
---|
203 | rect.width = rdouble(width) |
---|
204 | rect.height = rdouble(height) |
---|
205 | rect.theZ = rint(0) |
---|
206 | rect.theT = rint(0) |
---|
207 | |
---|
208 | # link the rectangle to the ROI and save it |
---|
209 | rect.setRoi(r) |
---|
210 | r.addShape(rect) |
---|
211 | self.updateService.saveAndReturnObject(rect) |
---|
212 | |
---|
213 | |
---|
214 | def getRectangles(session, imageId): |
---|
215 | """ Returns (x, y, width, height) of each rectange ROI in the image """ |
---|
216 | |
---|
217 | rectangles = [] |
---|
218 | shapes = [] # string set. |
---|
219 | |
---|
220 | roiService = session.getRoiService() |
---|
221 | result = roiService.findByImage(imageId, None) |
---|
222 | |
---|
223 | rectCount = 0 |
---|
224 | for roi in result.rois: |
---|
225 | for shape in roi.copyShapes(): |
---|
226 | if type(shape) == omero.model.RectI: |
---|
227 | x = shape.getX().getValue() |
---|
228 | y = shape.getY().getValue() |
---|
229 | width = shape.getWidth().getValue() |
---|
230 | height = shape.getHeight().getValue() |
---|
231 | rectangles.append((int(x), int(y), int(width), int(height))) |
---|
232 | continue |
---|
233 | return rectangles |
---|
234 | |
---|
235 | |
---|
236 | def downloadImage(session, imageId, imageName): |
---|
237 | """ |
---|
238 | This method downloads the first (only?) plane of the OMERO image and saves it as a local image. |
---|
239 | |
---|
240 | @param session The OMERO session |
---|
241 | @param imageId The ID of the image to download |
---|
242 | @param imageName The name of the image to write. If no path, saved in the current directory. |
---|
243 | """ |
---|
244 | queryService = session.getQueryService() |
---|
245 | rawPixelStore = session.createRawPixelsStore() |
---|
246 | |
---|
247 | # get pixels with pixelsType |
---|
248 | query_string = "select p from Pixels p join fetch p.image i join fetch p.pixelsType pt where i.id='%d'" % imageId |
---|
249 | pixels = queryService.findByQuery(query_string, None) |
---|
250 | theX = pixels.getSizeX().getValue() |
---|
251 | theY = pixels.getSizeY().getValue() |
---|
252 | |
---|
253 | # get the plane |
---|
254 | theZ, theC, theT = (0,0,0) |
---|
255 | pixelsId = pixels.getId().getValue() |
---|
256 | bypassOriginalFile = True |
---|
257 | rawPixelStore.setPixelsId(pixelsId, bypassOriginalFile) |
---|
258 | plane2D = scriptUtil.downloadPlane(rawPixelStore, pixels, theZ, theC, theT) |
---|
259 | |
---|
260 | plane2D.resize((3135, 2875)) |
---|
261 | p = Image.fromarray(plane2D) |
---|
262 | p.show() |
---|
263 | p.save(imageName) |
---|
264 | |
---|
265 | return (theX, theY) |
---|
266 | |
---|
267 | if __name__ == "__main__": |
---|
268 | |
---|
269 | # start by logging in to server |
---|
270 | client = omero.client("localhost") |
---|
271 | session = client.createSession("root", "omero") |
---|
272 | |
---|
273 | imageId = 969 |
---|
274 | |
---|
275 | # download the image as a local temp image |
---|
276 | #image_name = "tempImage.dat" |
---|
277 | image_name = "newTestImage.tiff" |
---|
278 | imgW, imgH = downloadImage(session, imageId, image_name) |
---|
279 | |
---|
280 | #showImage(image_name) |
---|
281 | print "image downloaded" |
---|
282 | |
---|
283 | |
---|
284 | # get list of ROI boxes as (x, y, width, height) on the image |
---|
285 | boxes = getRectangles(session, imageId) |
---|
286 | if len(boxes) == 0: |
---|
287 | print "No ROIs found - exiting!" |
---|
288 | import sys |
---|
289 | sys.exit() |
---|
290 | |
---|
291 | # use the width of the first box as the box_size (all should be same w,h) |
---|
292 | x,y,w,h = boxes[0] |
---|
293 | box_size = w |
---|
294 | |
---|
295 | # create a 'target' which will save the generated boxes as ROI rectangles to OMERO |
---|
296 | target = Target(box_size, session, imageId) |
---|
297 | omeroBoxer = OmeroSwarmTool(target, particle_diameter=box_size) # pass target to Boxer |
---|
298 | |
---|
299 | # add the reference boxes to the boxer |
---|
300 | for box in boxes: |
---|
301 | x, y, w, h = box |
---|
302 | x += box_size/2 # convert from top-left of ROI (OMERO) to centre of particle (EMAN2) |
---|
303 | y += box_size/2 |
---|
304 | y = imgH - y # convert distance from Top of image (OMERO) to distance from bottom (EMAN2) |
---|
305 | omeroBoxer.add_ref(x,y,image_name) |
---|
306 | |
---|
307 | # perform auto-boxing - results are written back to server, as ROIs on the image. |
---|
308 | omeroBoxer.auto_box(image_name) |
---|
309 | |
---|
310 | |
---|