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