1 /*****************************************************************************
2 Major portions of this software are copyrighted by the Medical College
3 of Wisconsin, 1994-2000, and are released under the Gnu General Public
4 License, Version 2. See the file README.Copyright for details.
5 ******************************************************************************/
6
7 #include "afni.h"
8
9 #ifndef ALLOW_PLUGINS
10 # error "Plugins not properly set up -- see machdep.h"
11 #endif
12
13 /***********************************************************************
14 Simple plugin to cluster data and return new dataset
15 Author: RW Cox
16
17 Mod: Added Erode/Dilate option to sever narrow connecting path
18 between clusters, by first eroding the outer layer of voxels,
19 then restoring voxels near the main body of the cluster.
20 Also, added 'Type' option to control processing of data
21 inside clusters.
22 Author: B. Douglas Ward
23 Date: 19 June 1998
24
25 ************************************************************************/
26
27 static char * CLUST_main( PLUGIN_interface * ) ;
28
29 static char helpstring[] =
30 " Purpose: Apply the clustering algorithm to a functional dataset.\n"
31 " Inputs:\n"
32 " Dataset = Functional dataset that must already be in memory\n"
33 " (not warp-on-demand) -- this is required.\n"
34 " Params = Type determines method for setting voxel intensities \n"
35 " within a cluster. \n"
36 " Radius controls the maximum distance between two nonzero\n"
37 " voxels for them to be considered neighbors in a cluster.\n"
38 " MinVol controls the minimum volume of a cluster that will\n"
39 " be accepted.\n"
40 " Erode/Dilate = Sever narrow connecting paths between clusters. \n"
41 " % Voxels Min. % of active 'neighbors' for a voxel to survive. \n"
42 " Dilate Restore voxels near main body of cluster. \n"
43 " Threshold = If the input dataset has a threshold sub-brick attached,\n"
44 " this option can be used to set its level.\n"
45 " Output = If this option is used, then a new dataset will be made\n"
46 " from the clusters. In that case, a prefix for the\n"
47 " new dataset filename must be provided. If this option\n"
48 " is not used, then the clustering results will overwrite \n"
49 " the existing dataset in memory and on disk.\n"
50 "Author -- RW Cox"
51 ;
52
53 /***********************************************************************
54 Set up the interface to the user
55 ************************************************************************/
56
57
58 DEFINE_PLUGIN_PROTOTYPE
59
PLUGIN_init(int ncall)60 PLUGIN_interface * PLUGIN_init( int ncall )
61 {
62 PLUGIN_interface * plint ;
63
64 /*----- plugin option labels -----*/ /* 19 June 1998 */
65 char * boolean_types[2] = {"False", "True"};
66 char * cluster_types[7] = {"Keep", "Mean", "Max", "AMax", "SMax", "Size",
67 "Order"};
68
69 if( ncall > 0 ) return NULL ; /* only one interface */
70 CHECK_IF_ALLOWED("3DCLUSTER","3D Cluster") ; /* 30 Sep 2016 */
71
72 /*-- set titles and call point --*/
73
74 plint = PLUTO_new_interface( "3D Cluster" , "Dataset Clustering" , helpstring ,
75 PLUGIN_CALL_VIA_MENU , CLUST_main ) ;
76
77 PLUTO_add_hint( plint , "Dataset Clustering" ) ;
78
79 PLUTO_set_sequence( plint , "A:afniinfo:dsetcluster" ) ;
80
81 /*-- first line of input: Dataset --*/
82
83 PLUTO_add_option( plint , "Dataset" , "Dataset" , TRUE ) ;
84 PLUTO_add_dataset(plint , "Function" ,
85 ANAT_ALL_MASK , FUNC_ALL_MASK ,
86 DIMEN_3D_MASK | BRICK_ALLREAL_MASK ) ;
87 PLUTO_add_hint( plint , "Choose input dataset" ) ;
88
89 /*-- second line of input: Cluster Parameters --*/
90
91 PLUTO_add_option( plint , "Params" , "Params" , TRUE ) ;
92 PLUTO_add_hint( plint , "Find and reject small clusters" ) ;
93 PLUTO_add_string (plint, "Type", 7, cluster_types, 0); /* 19 June 1998 */
94 PLUTO_add_hint( plint , "How to process data inside clusters" ) ;
95 PLUTO_add_number( plint , "Radius(mm)" , 0, 100,1 , 20,TRUE ) ;
96 PLUTO_add_hint( plint , "Max distance between 'neighbors'" ) ;
97 PLUTO_add_number( plint , "MinVol(ul)" , 0,1000,-1,100,TRUE ) ;
98 PLUTO_add_hint( plint , "Min size for cluster to survive" ) ;
99
100
101 /*---- 3rd line of input: Erosion/Dilation option ----*/ /* 19 June 1998 */
102 PLUTO_add_option (plint, "Erode/Dilate", "Erode/Dilate", FALSE);
103 PLUTO_add_hint (plint , "Sever narrow connecting paths between clusters");
104 PLUTO_add_number (plint, "% Voxels", 0, 100, 0, 50, TRUE);
105 PLUTO_add_hint (plint ,
106 "Min % of active 'neighbors' for a voxel to survive");
107 PLUTO_add_string (plint, "Dilate?", 2, boolean_types, 0);
108 PLUTO_add_hint (plint , "Restore voxels near main body of cluster");
109
110
111 /*-- fourth line of input: Threshold (optional) --*/
112
113 PLUTO_add_option( plint , "Threshold" , "Threshold" , FALSE ) ;
114 PLUTO_add_hint( plint , "Zero out if threshold brick too small" ) ;
115 PLUTO_add_number( plint , "Cutoff" , 0,1000,2 , 50,TRUE ) ;
116 PLUTO_add_hint( plint , "Threshold values < this => 0" ) ;
117
118 /*-- fifth line of input: Prefix for output dataset --*/
119
120 PLUTO_add_option( plint , "Output" , "Output" , FALSE ) ;
121 PLUTO_add_string( plint , "Prefix" , 0,NULL , 19 ) ;
122 PLUTO_add_hint( plint , "Name output dataset" ) ;
123
124 return plint ;
125 }
126
127 /***************************************************************************
128 Main routine for this plugin (will be called from AFNI).
129 ****************************************************************************/
130
CLUST_main(PLUGIN_interface * plint)131 static char * CLUST_main( PLUGIN_interface * plint )
132 {
133 char * tag , * new_prefix ;
134 float rmm , vmul , thresh ;
135 MCW_idcode * idc ;
136 THD_3dim_dataset * dset , * new_dset ;
137 int ival , ityp , nbytes , nvals ;
138 EDIT_options edopt ;
139 void * new_brick , * old_brick ;
140
141 int clust_type; /* input cluster type option */ /* 19 June 1998 */
142 char * str; /* input string */
143 float pv; /* pv % voxels within rmm must be active */
144 int dilate; /* boolean for perform dilation of cluster voxels */
145
146
147 /*--------------------------------------------------------------------*/
148 /*----- Check inputs from AFNI to see if they are reasonable-ish -----*/
149
150 if( plint == NULL )
151 return "**********************\n"
152 "CLUST_main: NULL input\n"
153 "**********************" ;
154
155 tag = PLUTO_get_optiontag(plint) ;
156 if( tag==NULL || strcmp(tag,"Dataset") != 0 )
157 return "**********************************\n"
158 "CLUST_main: bad Dataset option tag\n"
159 "**********************************" ;
160
161 idc = PLUTO_get_idcode(plint) ;
162 dset = PLUTO_find_dset(idc) ;
163 if( dset == NULL )
164 return "*****************************\n"
165 "CLUST_main: bad input dataset\n"
166 "*****************************" ;
167
168 tag = PLUTO_get_optiontag(plint) ;
169 if( tag==NULL || strcmp(tag,"Params") != 0 )
170 return "*********************************\n"
171 "CLUST_main: bad Params option tag\n"
172 "*********************************" ;
173
174 str = PLUTO_get_string(plint); /* 19 June 1998 */
175 if (strcmp(str,"Keep") == 0) clust_type = ECFLAG_SAME;
176 else if (strcmp(str,"Mean") == 0) clust_type = ECFLAG_MEAN;
177 else if (strcmp(str,"Max") == 0) clust_type = ECFLAG_MAX;
178 else if (strcmp(str,"AMax") == 0) clust_type = ECFLAG_AMAX;
179 else if (strcmp(str,"SMax") == 0) clust_type = ECFLAG_SMAX;
180 else if (strcmp(str,"Size") == 0) clust_type = ECFLAG_SIZE;
181 else if (strcmp(str,"Order") == 0) clust_type = ECFLAG_ORDER;
182 else if (strcmp(str,"Depth") == 0) clust_type = ECFLAG_DEPTH;
183 else
184 return
185 "**********************************\n"
186 "CLUST_main: Illegal Cluster option\n"
187 "**********************************";
188
189 rmm = PLUTO_get_number(plint) ;
190 vmul = PLUTO_get_number(plint) ;
191 if( rmm <= 0 || vmul <= 0 )
192 return "****************************\n"
193 "CLUST_main: bad Params input\n"
194 "****************************" ;
195
196
197 /* 19 June 1998 */
198 tag = PLUTO_peek_optiontag(plint) ;
199 if( tag != NULL && strcmp(tag,"Erode/Dilate") == 0 )
200 {
201 PLUTO_next_option(plint) ;
202 pv = PLUTO_get_number(plint);
203 if ((pv > 0.0) && (rmm <= 0.0))
204 return
205 "*******************************************************\n"
206 "CLUST_main: Erode/Dilate requires use of Cluster option\n"
207 "*******************************************************";
208 else
209 pv = pv / 100.0;
210
211 str = PLUTO_get_string(plint);
212 if (strcmp (str, "True") == 0)
213 {
214 if (pv <= 0.0)
215 return
216 "***********************************************\n"
217 "CLUST_main: Dilate requires use of Erode option\n"
218 "***********************************************";
219 else
220 dilate = 1;
221 }
222 else
223 dilate = 0;
224 }
225 else
226 {
227 pv = 0.0;
228 dilate = 0;
229 }
230
231
232 tag = PLUTO_peek_optiontag(plint) ;
233 if( tag != NULL && strcmp(tag,"Threshold") == 0 ){
234 PLUTO_next_option(plint) ;
235 thresh = PLUTO_get_number(plint) ;
236 if( thresh < 0.0 )
237 return "*******************************\n"
238 "CLUST_main: bad Threshold input\n"
239 "*******************************" ;
240
241 if( thresh > 0.0 && DSET_THRESH_INDEX(dset) < 0 )
242 return "**********************************************\n"
243 "CLUST_main: Dataset has no threshold sub-brick\n"
244 "**********************************************" ;
245 } else {
246 thresh = 0.0 ;
247 }
248
249 tag = PLUTO_peek_optiontag(plint) ;
250 if( tag != NULL && strcmp(tag,"Output") == 0 ){
251 PLUTO_next_option(plint) ;
252 new_prefix = PLUTO_get_string(plint) ;
253 if( ! PLUTO_prefix_ok(new_prefix) )
254 return "**********************\n"
255 "CLUST_main: bad prefix\n"
256 "**********************" ;
257 } else {
258 new_prefix = NULL ;
259 }
260
261 /*------------------------------------------------------*/
262 /*---------- At this point, the inputs are OK ----------*/
263
264 /*-- maybe, make a new dataset --*/
265
266 if( new_prefix == NULL ){ /* no prefix => edit input in place */
267
268 new_dset = dset ;
269 DSET_load( dset ) ; /* load into memory */
270
271 } else { /* OK, make a copy first */
272 new_dset = PLUTO_copy_dset( dset , new_prefix ) ;
273
274 if( new_dset == NULL )
275 return "****************************************\n"
276 "CLUST_main: failed to copy input dataset\n"
277 "****************************************" ;
278
279 DSET_unload( dset ) ; /* unload from memory */
280 }
281
282 /*-- set up for dataset editing --*/
283
284 INIT_EDOPT( &edopt ) ;
285
286 edopt.edit_clust = clust_type; /* 19 June 1998 */
287
288 edopt.clust_rmm = rmm ;
289 edopt.clust_vmul = vmul ;
290
291 edopt.erode_pv = pv;
292 edopt.dilate = dilate;
293
294 if( thresh > 0.0 ) edopt.thresh = thresh ;
295
296 /*-- edit the new dataset --*/
297
298 EDIT_one_dataset( new_dset , &edopt ) ;
299
300 /*-- if we made a completely new dataset, give it to AFNI --*/
301
302 if( new_dset != dset ){
303 ival = PLUTO_add_dset( plint , new_dset , DSET_ACTION_MAKE_CURRENT ) ;
304
305 if( ival ){
306 THD_delete_3dim_dataset( new_dset , False ) ;
307 return "**********************************************\n"
308 "CLUST_main: failure to add new dataset to AFNI\n"
309 "**********************************************" ;
310 }
311 } else {
312 DSET_overwrite( new_dset ) ; /* otherwise, re-write to disk */
313 PLUTO_force_redisplay() ; /* and force a redisplay of images */
314 }
315
316 /*-- done successfully!!! --*/
317
318 return NULL ;
319 }
320