-
Notifications
You must be signed in to change notification settings - Fork 2
/
Viveca Stitching Tool
307 lines (268 loc) · 13.9 KB
/
Viveca Stitching Tool
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/*
VIVECA STITCHING
Started in January 2019
Macro build and maintained by Vasco Fachada ([email protected])
At the moment this macro is using the stitching plugin "Grid/Collection stitching" used in Preibisch et al. Bioinformatics (2009).
This macro basically goes throught the folder of the user's choice and opens .czi files.
The metadata from the .czi is read and the individual tiles of the file are saved as orderly tiff files in a temp folder.
The read metada is used to feed the mentioned plugin the parameters for stitching. These include number of tiles in each axis.
The laser excitation data is also read and the image should display: 405->Blue; 488->Green; 555->Orange; 639->Red.
The tile overlap data is also read from the metdata and fed into the stitching plugin
Z-stack stiching is supported
Both modes of tile scanning in ZEN black are supported, uni and bidirectional
*/
close("*");
Dialog.create("Ok, grab your needle. This is just like knitting");
Dialog.addMessage("Welcome to our Viveca stitching tool. \n");
Dialog.addMessage("\nThis macro is mainly aimed for stitching tiled-images produced by our Zeiss LSM700. \nAt the moment, stitching is only working with .czi files which cannot contain spaces in their file name.");
Dialog.addCheckbox(" Display your images once they are stitched?", true);
Dialog.addCheckbox("\n My tiles were scanned in 'zigzag / snake / bidirection' mode", false);
Dialog.addMessage("Constantly under development. Please consider acknowledging the author in publications. \nAny issues should be reported in order to improve the tool. \n \nauthor: Vasco Fachada (vasco.fachada(at)gmail.com)");
Dialog.show();
visualize_checkBox = Dialog.getCheckbox();
tilingMode_checkBox = Dialog.getCheckbox();
//Finding the .czi files to stitch
dir = getDirectory("Select a directory containing your CZI files you want to stitch");
start = getTime();
files = getFileList(dir);
outputFold = dir+"Stitchings";
File.makeDirectory(outputFold);
setBatchMode(true);
//Deleting any previously produced folders in the case the macro broke half-way
for(f=0; f<files.length; f++) {
if(matches(files[f], ".*stitch_temp.*")){
old_temp_folder = dir+File.separator+files[f];
old_content = getFileList(old_temp_folder);
for (li=0; li<old_content.length; li++){
old_images = getFileList(old_temp_folder+File.separator+old_content[li]);
for (lii=0; lii<old_images.length; lii++){
File.delete(old_temp_folder+File.separator+old_content[li]+File.separator+old_images[lii]);
}
File.delete(old_temp_folder+File.separator+old_content[li]);
}
File.delete(old_temp_folder);
print("successfully deleted previously produced temp files");
}
}
k=0;
n=0;
Tot_nmrOfTilesOpened = 0;
nmrOfFilesStitched = 0;
resultArray = newArray(0);
pixelTotal = 0;
sizeLoaded = 0;
run("Bio-Formats Macro Extensions");
for(f=0; f<files.length; f++) {
if(endsWith(files[f], ".czi")) {
nmrOfTilesOpened = 0;
k++;
id = dir+files[f];
//print(files[f]);
//waitForUser;
Ext.setId(id);
Ext.getSeriesCount(seriesCount);
n+=seriesCount;
//READING METADATA
run("Bio-Formats Importer", "open=["+id+"] color_mode=Default view=Hyperstack stack_order=XYCZT use_virtual_stack series_"+(1));
fullMeta=getMetadata("Info");
//Determining if 2D or 3D
zSteps = parseInt(substring(fullMeta, indexOf(fullMeta, "SizeZ =")+8, indexOf(fullMeta, "SizeZ =")+10));
print("z-steps: "+zSteps);
//Determining if timeseries
tPoints = parseInt(substring(fullMeta, indexOf(fullMeta, "SizeT =")+8, indexOf(fullMeta, "SizeT =")+9));
print("timepoints: "+tPoints);
//Determining number of positions. Ine the case of the 12-well plate container functions in LSM700 Zen black, each position corresponds to each well.
if (indexOf(fullMeta, "Information|Image|SizeS #1 =") > -1){
positionsNum = substring(fullMeta, lastIndexOf(fullMeta, "Information|Image|SizeS #1 =")+lengthOf("Information|Image|SizeS #1 =")+0, lastIndexOf(fullMeta, "Information|Image|SizeS #1 =")+lengthOf("Information|Image|SizeS #1 =")+3);
positionsNum = parseInt(positionsNum);
}else
positionsNum = 1;
print("number of positions: "+positionsNum);
//Getting the number of tiles in each axis for later stitching steps
posStartInd = 3+lengthOf(toString(positionsNum));
posLastInd = 4+lengthOf(toString(positionsNum));
gridMetaX = substring(fullMeta, lastIndexOf(fullMeta, "PositionGroup|TilesX #")+lengthOf("PositionGroup|TilesX #")+posStartInd, lastIndexOf(fullMeta, "PositionGroup|TilesX #")+lengthOf("PositionGroup|TilesX #")+posLastInd);
gridMetaY = substring(fullMeta, lastIndexOf(fullMeta, "PositionGroup|TilesY #")+lengthOf("PositionGroup|TilesY #")+posStartInd, lastIndexOf(fullMeta, "PositionGroup|TilesY #")+lengthOf("PositionGroup|TilesY #")+posLastInd);
gridX = parseInt(gridMetaX);
print("X grid: "+gridX);
gridY = parseInt(gridMetaY);
print("Y grid: "+gridY);
TilesPerPos = gridX * gridY;
print("Tiles per position: "+TilesPerPos);
TotalNumTiles = TilesPerPos * positionsNum * tPoints;
print("total number of tiles: "+TotalNumTiles);
if (TotalNumTiles < 10)
mosaicSize = "{i}";
else if (TotalNumTiles > 9 && TotalNumTiles < 100)
mosaicSize = "{ii}";
else if (TotalNumTiles > 99 && TotalNumTiles < 1000)
mosaicSize = "{iii}";
else
mosaicSize = "{iiii}";
print("mosaic size: "+mosaicSize);
//Reading tile scanning direction
if (tilingMode_checkBox==1)
tilingDirection = "[Grid: snake by rows] order=[Right & Down ]";
else
tilingDirection = "[Grid: row-by-row] order=[Right & Down ]";
print("Tiling direction: "+tilingDirection);
//Reading tile overlap
tlOv = substring(fullMeta, indexOf(fullMeta, "TileAcquisitionOverlap #")+28, indexOf(fullMeta, "TileAcquisitionOverlap #")+30);
if (tlOv == '0.')
tlOverlap = 100*parseFloat(substring(fullMeta, indexOf(fullMeta, "TileAcquisitionOverlap #")+28, indexOf(fullMeta, "TileAcquisitionOverlap #")+39));
else
tlOverlap = 0;
print("tile overlap: "+tlOverlap);
//Looking and determining number of channels in data
channels_nmr = parseInt(substring(fullMeta, indexOf(fullMeta, "SizeC =")+8, indexOf(fullMeta, "SizeC =")+9));
channels = newArray(channels_nmr);
colors = newArray(channels_nmr);
print(channels_nmr+" channels:");
for (ii=0; ii<channels_nmr; ii++){
laserUsed = substring(fullMeta, indexOf(fullMeta, "Attenuator|Laser #"+ii+1+" =")+27, indexOf(fullMeta, "Attenuator|Laser #"+ii+1+" =")+31);
channels[ii] = parseInt(laserUsed);
//print(channels[ii]);
if (channels[ii] == 405) colors[ii] = "Blue";
else if (channels[ii] == 488) colors[ii] = "Green";
else if (channels[ii] == 555) colors[ii] = "Yellow";
else if (channels[ii] == 639){
colors[ii] = "Red";
print(colors[ii]+" with "+laserUsed+" laser");
//else print(colors[ii]);
}else exit("While reading the metadata we have been unable to find which laser was used to scan one or more of the channels. \nPlease contact [email protected] for help.");
}
//waitForUser;
getVoxelSize(width, height, depth, unit); //detecting the voxel size of the current raw file, so the final image also has the same scale.
close();
//Creating temporary tiff files for analysis and organization of stiched file
for (l=1; l<positionsNum+1; l++){
savingDir_temp = outputFold+File.separator+"stitch_temp";
File.makeDirectory(savingDir_temp);
positionDir = savingDir_temp+File.separator+"position"+l;
File.makeDirectory(positionDir);
if (seriesCount > 1){
for (i=1; i<TilesPerPos+1; i++) {
run("Bio-Formats Importer", "open=["+id+"] color_mode=Default view=Hyperstack stack_order=XYCZT use_virtual_stack series_"+(i+nmrOfTilesOpened));
fullName = getTitle();
//dirName = substring(fullName, 0,lastIndexOf(fullName, ".czi"));
fileName = substring(fullName, lastIndexOf(fullName, " - ")+3, lengthOf(fullName));
//5print(fileName);
fileSeries = substring(fileName, indexOf(fileName, "#"), lengthOf(fileName)); //collects the number of series starting from the hashtag. #1 or #0001
saveAs("tiff", positionDir+File.separator+fileName+".tif");
close();
}
//STICHING AND VISUALIZATION
// setBatchMode(false);
//"Grid/Collection stitching" used in Preibisch et al. Bioinformatics (2009).
run("Grid/Collection stitching", "type="+tilingDirection+" grid_size_x="+gridX+" grid_size_y="+gridY+" tile_overlap="+tlOverlap+" first_file_index_i="+1+nmrOfTilesOpened+" directory="+positionDir+" file_names=["+substring(fileName, 0, lengthOf(fileName)-lengthOf(fileSeries))+"#"+mosaicSize+".tif] output_textfile_name=TileConfiguration.txt fusion_method=[Linear Blending] regression_threshold=0.30 max/avg_displacement_threshold=2.50 absolute_displacement_threshold=3.50 ignore_z_stage computation_parameters=[Save memory (but be slower)] image_output=[Fuse and display]");
//run("Grid/Collection stitching", "type= grid_size_x="+gridX+" grid_size_y="+gridY+" tile_overlap="+tlOverlap+" first_file_index_i="+1+nmrOfTilesOpened+" directory="+positionDir+" file_names=["+substring(fileName, 0, lengthOf(fileName)-lengthOf(fileSeries))+"#"+mosaicSize+".tif] output_textfile_name=TileConfiguration.txt fusion_method=[Linear Blending] regression_threshold=0.30 max/avg_displacement_threshold=2.50 absolute_displacement_threshold=3.50 ignore_z_stage computation_parameters=[Save memory (but be slower)] image_output=[Fuse and display]");
nmrOfTilesOpened = nmrOfTilesOpened + TilesPerPos;
//setBatchMode(false);
//for (xx=0;xx<colors.length;xx++){
//print(colors[xx]);
//}
if (isOpen('Fused') == 1)
print('Stiching successful!');
else{
waitForUser('There was a problem. \nCheck for instance if you have spaces in your file path, there should be none. \nRestart the stitching.');
}
if (colors.length<2)
run(colors[0]);
else{
for (ii=0; ii<colors.length; ii++){ //Assigning colors to final image corresponding to excitation laser wavelengths
Stack.setDisplayMode("color");
Stack.setChannel(ii+1);
run(colors[ii]);
}
run("Make Composite", "stack");
}
//Setting the native scale
run("Properties...", "channels="+channels_nmr+" slices="+zSteps+" frames="+tPoints+" unit="+unit+" pixel_width="+width+" pixel_height="+height+" voxel_depth="+depth+"");
//Stack.setDisplayMode("composite");
savingDir = outputFold+File.separator+"STITCHED_"+substring(files[f], 0, lengthOf(files[f])-4);
File.makeDirectory(savingDir);
saveAs("tiff", savingDir+File.separator+"STITCHED_"+substring(files[f], 0, lengthOf(files[f])-4)+"_pos"+l+".tif"); //saving stiched image
saveType = "CHED_";
pixelTotal = pixelTotal+ (getHeight()*getWidth());
imgInfo=getImageInfo();
sizeLoaded = sizeLoaded + parseInt(substring(imgInfo, lastIndexOf(imgInfo, "Size:")+7, lastIndexOf(imgInfo, "MB")));
nmrOfFilesStitched = nmrOfFilesStitched+1;
//close("Fused");list[st]
list = getFileList(positionDir);
for (li=0; li<list.length; li++){
File.delete(positionDir+File.separator+list[li]); //deleting individual files created. The original data is still intact in the .czi files
File.delete(positionDir); //deletes folder
File.delete(savingDir_temp); //deletes folder
}
}
}
Tot_nmrOfTilesOpened = Tot_nmrOfTilesOpened + TotalNumTiles;
resultArray = append(resultArray, savingDir);
//print("result array: "+resultArray[k-1]);
//print("result array length: "+resultArray.length);
}
}
//print(f);
if (k==0)
exit("Sorry, there seems to be no .czi files in \n"+dir+".");
else if (seriesCount == 1)
exit("Your .czi file(s) in \n"+dir+" \ndon't seem to have more than 1 tile.");
close("*");
for (rA=0; rA<resultArray.length; rA++){
//print(resultArray[rA]);
//print("array length is: "+resultArray.length);
//print("current array is: "+resultArray[rA]);
list = getFileList(resultArray[rA]);
for (st=0; st<list.length; st++){
if (matches(list[st], ".*STIT"+saveType+".*")){
//print(resultArray[rA]+File.separator+list[st]);
curTil = resultArray[rA]+File.separator+list[st];
open(curTil);
//run("Color Balance...");
run("RGB Color", "slices");
title=getTitle();
//print(title);
title=substring(title,0,lengthOf(title)-13);
//print(title);
//print(curTil);
//print(list[st]);
close(title);
}
}
if (positionsNum>1){
//setBatchMode(false);
run("Images to Stack", "name=Stack title="+title+" keep");
colNum = list.length/2;
rowNum = list.length/colNum;
run("Make Montage...", "columns="+colNum+" rows="+rowNum+" scale="+1/positionsNum+" border=4 font=30 label");
saveAs("tiff", resultArray[rA]+File.separator+"Montage.tif"); //saving stiched image
close("stack");
if(visualize_checkBox==1){
setBatchMode(false);
//open(resultArray[rA]+File.separator+"Montage.tif");
//run("Despeckle", "stack");
//run("Enhance Contrast...", "saturated=0.1 process_all use");
}
}
else{
if(visualize_checkBox==1){
//print(resultArray[rA]);
//print(list[0]);
setBatchMode(false);
//print(curTil);
open(curTil);
}
}
}
end = getTime();
Dialog.create("This is not good for Zeiss business...");
Dialog.addMessage("You just stitched "+Tot_nmrOfTilesOpened+" Tile(s) into "+nmrOfFilesStitched+" Mosaic position(s) from "+k+" files.\n \nThat's a total of "+pixelTotal+" pixels and "+sizeLoaded+" MB... \n...within "+(end-start)/1000+" seconds ("+(end-start)/1000/60+" minutes).\n \nYou know, that's Super Computer kind of stuff...clap clap!");
Dialog.show();
//function to append data to expanding arrays
function append(arr, value) {
arr2 = newArray(arr.length+1);
for (i=0; i<arr.length; i++)
arr2[i] = arr[i];
arr2[arr.length] = value;
return arr2;
}